S.O.L.I.D, Amor e Carinho

Recentemente, me deparei com uma postagem que dizia: “todo desenvolvedor que não partilha conhecimento está fadado à estagnação”, após muito refletir, decidi compartilhar um pouco sobre um assunto que comecei a estudar nessas últimas semanas, espero que gostem.
Introdução
Todo indivíduo possui princípios os quais guiam suas decisões durante o longo percurso da vida. Alguns têm como princípio não mentir, outros de não comer carne, enfim, são esses princípios que definem como a vida é vivida e, portanto, devem ser muito bem escolhidos.
Como o código imita a vida, ele também precisa de princípios que irão definir qual caminho seguir, o do caos e ruína, ou o da organização. Em geral, há 3 pilares na vida de um código: amor, carinho e uma base. O primeiro caminho, que não é muito difícil de ser atingido, é consequência da falta desses pilares. Já o segundo caminho, pode ser alcançado com amor, carinho e, principalmente, uma base sólida (olha a piadinha aqui :D).
A parte fácil é dar amor e carinho, já a base é um pouco mais complicada. Para ajudar a vida do programador médio, Tio Bob (aka. Uncle Bob) introduziu uma série de princípios para a Programação Orientada a Objetos (POO) que devem ser seguidos para alcançar um código limpo e organizado, esses princípios formam o S.O.L.I.D, sendo eles:
1. Single Responsability Principle | Princípio da Responsabilidade Única (SRP);
2. Open-Close Principle | Princípio Aberto-Fechado (OCP);
3. Liskov Substitution Principle | Princípio da Substituição de Liskov (LSP);
4. Interface Segregation Principle | Princípio da Segregação de Interfaces (ISP);
5. Dependency Inversion Principle | Princípio da Inversão de Dependências (DIP).
Single Responsibility Principle
O SRP é o primeiro, e provavelmente, o mais importante dos 5 princípios. Esse princípio diz que uma classe só deve possuir uma responsabilidade. Apesar de esse ser um conceito relativamente fácil de ser compreendido, ele pode se tornar difícil de ser aplicado. A primeira grande dificuldade é definir o que é uma “responsabilidade”. Responsabilidade é um termo abstrato que pode variar de pessoa para pessoa, logo a probabilidade de existir divergência sobre a definição “certa” entre pessoas do time é grande.
Para resolver esse problema, Uncle Bob definiu “Responsabilidade” como: Quantos motivos a classe tem para mudar? Se ela conter mais de um motivo, provavelmente está ferindo esse princípio.

O exemplo acima mostra a mesma classe ferindo e seguindo o SRP. Repare, no exemplo sem SRP, que caso haja uma mudança no método pagarDebito(valor), teríamos que modificar a classe ContaCorrente, ou seja, esse é um motivo para mudança (a primeira responsabilidade da classe). Da mesma forma, caso os métodos pagarCredito(valor) ou imprimirExtrato() mudem, também seriam necessárias modificações na classe!
Nesse caso em específico, a ContaCorrente possui 3 motivos para mudar, logo o princípio é ferido. Por ser ferido, alguns problemas podem acontecer no médio/longo prazo por conta do alto acoplamento do código.
Então, para que o problema seja resolvido, é interessante que dividir as responsabilidades em objetos únicos, fazendo com que o objeto “ContaCorrente” não precise ser modificado caso alguma das operações precise de mudança.
Repara que no exemplo com SRP, as responsabilidades foram divididas em objetos, assim caso haja uma mudança na maneira com que o pagamentoDebito(valor) funciona, a ContaCorrente não precisará ser modificada.
Em suma, esse princípio além de desacoplar o código, torna as classes mais coesas e concisas, aumentando a reusabilidade e flexibilidade do código.
Open-Close Principle
O OCP introduz a ideia de que toda classe deve ser aberta para extensão, e fechada para modificação, porém proibir a modificação de uma classe é virtualmente impossível, uma vez que não há como prever se existirão mais funcionalidades, ou que não existirão bugs. Portanto, eu gosto de definir o OCP como: proteger o código existente de potenciais mudanças.
Vendo esse cenário, onde as classes precisavam ser protegidas do terrível monstro da mudança, o deus criador dos objetos criou o polimorfismo. O polimorfismo nos ajuda a seguir o princípio utilizando herança e/ou interfaces, assim, sempre que uma nova funcionalidade for adicionada, não há modificação do código antigo, apenas criação de código novo!
Em suma, esse princípio busca blindar as classes que já existem de potenciais mudanças que podem acontecer em seu ciclo de vida, e para isso utilizamos abstrações. É importante salientar que, apesar de muito útil, esse princípio precisa ser utilizado com cuidado, além de possuir uma grande relação com o último princípio, DIP.
Liskov Substitution Principle
O LSP é, talvez, o mais complicado dos princípios, sendo baseado em uma publicação acadêmica da Professora Doutora Barbara Liskov. Esse princípio tem como objetivo definir como utilizar herança da maneira correta.
A ideia de “Classes derivadas podem substituir as classes bases” é o norte desse princípio. Em sua publicação, Liskov cria uma série de regras (com provas matemáticas e tudo!) que, se seguidas, garantem que uma classe é um subclasse da outra. Um exemplo ilustrando o conceito de classes e subclasses é dado a seguir.
Seriam os Retângulos uma subclasse dos Quadrados? A resposta natural é dizer que sim, uma vez que um retângulo parece ser um caso especial de quadrado, porém as coisas não são tão simples.
Um quadrado possui 4 lados, 4 ângulos retos e todas as arestas com mesmo comprimento. Já um retângulo, apesar de possuir 4 lados e 4 ângulos retos, não tem como característica a igualdade do comprimento de suas arestas.
Sendo assim, como não é possível substituir um quadrado por um retângulo em todos os casos, é possível afirmar que um retângulo NÃO É uma subclasse do quadrado.
O exemplo dos quadrados e retângulos ajuda a entender o conceito do LSP, porém é necessário generaliza-lo. Essa generalização é constituída por 7 regras, sendo elas:
Contra variância de argumento: os métodos da classe derivada precisam possuir os mesmos parâmetros da classe base;
- Covariância dos resultados: retornos dos métodos da classe derivada devem ser os mesmos da classe base;
- Regra da Exceção: os métodos da classe derivada NÃO PODEM lançar exceções que a classe base não conheça;
- Pré-condição: as pré-condições da classe derivada NÃO PODEM ser mais fortes do que as da classe base. Por exemplo: se o método, na classe base, não aceita null como parâmetro, o método da classe derivada também não pode aceitar;
- Pós-condição: as pós-condições da classe derivada NÃO PODEM ser mais fracas que as da classe base. Por exemplo, se o método na base não sabe tratar null, e o método da derivada envia null, haverá um problema;
- Regra da Invariância: Invariância é uma afirmação sobre uma propriedade específica de uma Classe que nunca muda. Um exemplo de invariância pode ser dado com uma pilha, nela, a inserção de elementos SEMPRE ocorre no topo. Essa é uma característica da pilha, e não pode ser mudada. Nessa regra, todas as invariâncias da classe base devem ser seguidas pela classe derivada;
- Regra da Limitação (Constraints): propriedades da classe que evoluem com o tempo/uso. Usando novamente a pilha como exemplo, o tamanho máximo da pilha é uma propriedade que varia de pilha para pilha, ou seja, essa propriedade é inerente à instância da pilha, e não específica da classe. O tamanho máximo de uma pilha é um exemplo de constraint. Nessa regra, todas as constraints da classe base são passadas para as derivadas;
Em suma, seguir esse princípio garante que ao usar herança, os subtipos possam ser utilizadas pelos supertipos.
Interface Segregation Principle
O ISP introduz a ideia de que as classes derivadas só devem implementar os métodos que irão utilizar ao fazer uso de interfaces. Ou seja, uma classe que segue o ISP não deverá implementar métodos que não são utilizados, uma vez que esse tipo de comportamento, além de poluir o código, torna-o menos conciso e legível.
Ao contrário do princípio anterior, o ISP é mais simples e fácil de ser utilizado. É interessante que o ISP seja seguido, tornando as dependências da classe mais visíveis e limpas. Essa limpeza faz com que seja possível entender o que o código faz, em grande parte das vezes, apenas olhando pra suas dependências, o que pode poupar tempo (e como sabemos, tempo é dinheiro ;) ).
Dependency Inversion Principle
O último dos princípios, o DIP tem como objetivo inverter o fluxo de dependências da classe. Para poder inverter o fluxo, primeiro é necessário entender o que é o fluxo. O fluxo é, basicamente, a ordem com que as coisas são são criadas, e quais as dependência entre cada componente.

Na Imagem 2, temos um fluxo unidirecional, onde a Classe A depende da Classe B, que depende da Classe C, e assim sucessivamente. As dependências são diretas, onde A conhece a implementação de B.
Para inverter o fluxo que é mostrada na Imagem 2 acima, usaremos interfaces! Com as interfaces, quem faz uso da classe não se importa com a implementação, mas sim sua abstração. Basicamente, não há mais dependência em detalhes, somente em abstrações.

A imagem acima exibe como o fluxo deve ficar após a adição da abstração, e com ela fica visível a razão do nome do princípio. Ao utilizar o DIP, atinge-se o OCP, evita-se dependências circulares e, consequentemente, aumenta a reusabilidade do código.
Apesar de parecer um campo de flores, nem tudo é perfeito. O uso de DIP também traz malefícios, que devem ser ponderados ao aplicar o princípio. Entre os malefícios, podemos citar o maior esforço em criar novos componentes, além de aumentar a complexidade do código.
Conclusão
Apesar de ser um assunto denso, é muito importante que todo desenvolvedor saiba sobre S.O.L.I.D. Assim como todo pai de primeira viagem, que busca estudar e entender como criar o filho que virá ao mundo, o desenvolvedor também precisa entender como criar um bom código.
O código, quando bem criado, afetará positivamente as pessoas com quem tiver contato ao decorrer de sua vida útil, causando menor custo, estresse e mais alegria, logo, é de suma importância que os conceitos de código limpo (que ainda terá um artigo!) sejam difundidos.
Apesar de esse ser nosso primeiro post, espero que tenha te ajudado de alguma forma. Muito obrigado pela atenção, críticas, correções e opiniões são muito bem-vindas!
Atenciosamente, Abe, do Time Mobyle.