Patterns & Practices

Olá,

bom, já temos conteúdo suficiente indexado pelo Google falando de SOLID. Minha intenção é criar um debate sobre SOLID e não definí-lo para você.

Single responsibility principle
Open/closed principle
Liskov substitution principle
Interface segregation principle
Dependency inversion principle

Ao longo desses muitos anos desenhando soluções tenho algumas dicas para facilitar a vida de quem está vendo S.O.L.I.D. pela primeira vez. Aos que já usam SOLID no dia-a-dia, é provável encontrar muitos que já se depararam com esses paradigmas em algum momento. Minha intenção é trazer à tona os problemas de interpretação, mostrar as minhas interpretações e evocar o bom senso para que possamos chegar a alguma conclusão satisfatória.

Single responsibility principle

Na programação orientada a objeto , o princípio da responsabilidade única afirma que cada classe deve ter uma única responsabilidade , e que a responsabilidade deve ser totalmente encapsulado pela classe . Todos os seus serviços devem ser estreitamente alinhados com essa responsabilidade” … “Martin define a responsabilidade como uma razão para mudar, e conclui que a classe ou módulo deve ter um, e apenas um, razão para mudar. Como exemplo, considere um módulo que compila e imprime um relatório. Tal módulo pode ser alterado, por duas razões . Em primeiro lugar, o conteúdo do relatório pode mudar. Em segundo lugar, o formato do relatório pode mudar. Essas duas coisas mudam por causas muito diferentes, um substantivo, e uma estética. O princípio responsabilidade única diz que estes dois aspectos do problema são realmente duas responsabilidades distintas, e, portanto, deve estar em classes ou módulos separados. Seria uma má concepção de acoplamento duas coisas que mudam por razões diferentes em momentos diferentes.

A razão pela qual é importante para manter uma classe focado numa única preocupação é que ele torna a classe mais robusta. Continuando com o exemplo anterior, se não houver uma mudança no processo de compilação relatório, há maior perigo de que o código de impressão vai quebrar se ele faz parte da mesma classe.

A respeito da SRP, não é difícil encontrar cenários em que você se questione sobre qual a granularidade de uma responsabilidade. Seguindo o exemplo do Fowler, podemos considerar a formatação (disposição) do conteúdo no relatório, o formato (extensão), o conteúdo, a forma de publicação, e diversos outros aspectos. Todos os aspectos precisam ser isolados? Admito que fazendo esse post acabo percebendo responsabilidade como algo muito mais granular do que eu acordei pensando ser. Mas retomo minhas indagações, e digo: Há a possibilidade de chegarmos ao doentio pensamento de que o posicionamento isolado de um label desse relatório pode se encaixar na frase “Seria uma má concepção de acoplamento duas coisas que mudam por razões diferentes em momentos diferentes.“. Então ficam perguntas sem resposta, onde parar? Até que ponto devemos abstrair?

Na minha opinião, a medida é evitar over-design, e refatorar sempre. Não há como você se abster de abstrações momentaneamente irrelevantes, sem voltar a estas quando ganham relevância. Outro pensamento muito importante é prever o que é óbvio. Algumas abstrações não fazem sentido na empresa A, mas fazem sentido na empresa B, e acredite, algumas decisões não levam em consideração estruturas programáticas, projeto, ou padrões, são tomadas com base no perfil do cliente. Se nesse caso do relatório, meu cliente é do tipo que exige 10 mudanças de layout em 1 relatório, vou optar por uma ferramenta de relatórios para abstrair todo o design. Se o relatório é de propriedade de 2 áreas, eu teria a tendência a isolar cada a view de cada uma das áreas, para viabilizar a abstração.

E você? #PensaNisso

Open/closed principle

Bertrand Meyer é geralmente creditado como tendo originado o termo Open/Closed Principle, que apareceu em seu livro de 1988 Object Oriented Software Construction. A idéia era que, uma vez concluída, a implementação de uma classe,esta só poderia ser modificada para corrigir erros; recursos novos ou alterados exigiria a criação de uma nova classe diferente. Essa classe pode reutilizar a codificação da classe original através de herança. A subclasse derivada pode ou não pode ter a mesma interface da classe original. A definição de Meyer defende herança de implementação. A implementação pode ser reutilizado através de herança, mas especificações de interface não precisam ser. A implementação existente está fechada para modificações e novas implementações não precisam implementar a interface existente.

Durante a década de 1990, o Open/Closed Principle tornou-se popularmente redefinido para se referir ao uso de interfaces abstratas, onde as implementações podem ser alterados e várias implementações poderiam ser criados e polimorficamente substituídos um pelo outro.Em contraste com o uso de Meyer, esta definição defende herança com base em classes abstratas. Especificações da interface podem ser reutilizadas através de herança, mas a implementação não precisa ser. A interface existente está fechada para modificações e novas implementações devem, no mínimo, implementar essa interface. 1996 o artigo de Robert C. Martin “The Open-Closed Principle” foi um dos escritos seminais para tomar essa atitude. Em 2001, Craig Larman relacionada ao Open/Closed Principle para o padrão por Alistair Cockburn chamado Variações Protegidas, e para a discussão David Parnas da ocultação de informações“.

Esse é um princípio nem tão complexo de se implementar, mas admito, descaradamente é o princípio do SOLID que mais negligencio. Uso outra abordagem para o tema: Dá para reaproveitar isoladamente, ou pelo menos consistentemente em derivações? As tomadas de decisão por modificar uma classe ou reescrevê-la completamente, ou mesmo quebrá-la em abstração e derivações são baseadas na idéia de reaproveitamento, unica e exclusivamente.

Não faz muito tempo, que criei uma abstração de file system para poder suportar em um processo o trabalho com FTP, GridFS do MongoDB e NTFS de forma transparente. O foco era o reaproveitamento e abstração do mecanismo que manipula arquivos, dando independência de repositório.

Interface segregation principle

O princípio de Segregação de Interface (ISP) afirma que nenhum cliente deve ser forçado a depender de métodos que não usam. ISP divide as interfaces que são muito grandes em partes menores e mais específicos para que os clientes só têm de saber sobre os métodos que são de interesse para eles. Tais interfaces de encolhidos são também chamados de interfaces de função. ISP tem a intenção de manter um sistema dissociado e, portanto, mais fácil de refatorar, alterar e redistribuir. ISP é um dos cinco princípios SOLID de projeto orientados a objetos, similar ao Princípio alta coesão, de GRASP.

ISP é um princípio que cultivo com amor e ódio, portanto uso com parcimônia. Assim como abordagem do Open/Close, somente utilizo segmentação de interfaces na medida que oferece melhor reaproveitamento, optando pelo meio termo entre coesão e necessidade de requisitantes. A repeito da utilização de contratos baseados em interface, somente os utilizo no nível mais amplo de abstração de comportamento, como em serviços, por exemplo, em abstrações cujo design oferece mais de uma implementação, mesmo que a implementação não contemple todos os itens de design. Uma enxurrada de interfaces para itens não abstraíveis é um desperdício.

Dependency inversion principle

Na programação orientada a objeto, o princípio da inversão dependência refere-se a uma forma específica de dissociação onde as relações de dependência convencionais estabelecidas a partir do nível mais alto, de definição de políticas para os módulos de baixo nível, módulos de dependência são invertidas com a finalidade de prestação de exibir módulos de alto nível independente dos módulos de baixo nível, omitindo detalhes de implementação. 
Os principais estados:
A. não deve depender de módulos de baixo nível. Ambos devem depender de abstrações.
B. Abstrações não devem depender de detalhes. Os detalhes devem depender de abstrações.
O princípio inverte a forma como algumas pessoas podem pensar sobre o design orientado a objeto, ditando que ambos os objetos de alto e de baixo nível deve depender da mesma abstração.”

Dependency injection

Injeção de dependência é um padrão de projeto de software que permite a remoção do prenchimento das dependências em hard-coded e tornando possível para mudá-las, seja em tempo de execução ou em tempo de compilação.

Pode ser usado, por exemplo, como uma maneira simples de carregar plugins dinamicamente ou escolher stubs ou objetos simulados em ambientes de teste ou objetos reais em ambientes de produção. Este padrão de projeto prega que injetaemos os elementos de dependência (objeto ou valor, etc) no destino automaticamente por saber a exigência do destino. Outro padrão, chamado de dependency lookup (pesquisa de dependência), é um processo regular e processo inverso para injeção de dependência.
Um de seus princípios fundamentais é a separação do comportamento de resolução de dependências.

De longe Dependency Inversion  é um dos princípios que mais gosto de usar no dia-a-dia. Embora Service Locator, seja um padrão que particularmente não gosto de usar, Dependency Injection  e Plugin são padrões que oferecem flexibilidade para de modelar soluções altamente desacopladas, de forma simples e descomplicada tornando o desenvolvimento altamente produtivo. Eximir os consumidores de conhecerem as dependências de um serviço ou entidade torna desenvolvimento muito mais fluido e gostoso. Sem contar que garante um código mais limpo.

O que você acha?

Comente, compartilhe, curta!

Logo abaixo desse texto você encontra os Posts Relacionados, e botões de compartilhamento, em seguida a sessão de comentários!

Gostou? Então aproveite para curtir, compartilhar e enviar comentários, dúvidas ou sugestões.

Conheça o Grupo Arquitetura de Softwate | .NET: Facebook e Telegram
Luiz Carlos Faria: Site, Youtube, Facebook, Twitter, Telegram, Linkedin e Email