Quando escuto ou leio a palavra SOLID me vem logo o Snake, do game Metal Gear Solid na cabeça e isso acontece há muito tempo. Não sou nenhum fã do jogo, mas também não sou hater, apenas não joguei o suficiente para entrar em um dos lados da força.
Agora, voltando ao tal do SOLID – que não é jogo -, você já ouviu falar dele? Já o usou? Sabe para que serve? Neste artigo vamos fazer um overview e ver a melhor forma de usar esse cara…
Primeiro precisamos saber que SOLID é uma sigla para Single responsibility, Open-closed, Liskov substitution, Interface segregation and Dependency inversion.
Para facilitar, o SOLID é um acrônimo para os cinco primeiros princípios da programação orientada para objetos, nós buscamos segui-los para que um projeto seja realmente orientado a objetos e não um fruto da nossa linda e maravilhosa criatividade.
Vamos ver o que cada um desses princípios tratam; só para facilitar o entendimento dos conceitos, utilizaremos alguns exemplos em JAVA e outros em Ruby:
– Single Responsibility
O Single responsibility está ligado diretamente ao objetivo que a cada classe possui, sendo que uma classe deve ter sempre um único objetivo. Se o sistema está crescendo e uma classe passou a ter duas responsabilidades é hora de dividi-la em duas classes, cada uma com uma responsabilidade. Exemplo:
class Client def save(args) super ExecuteDepencencies.execute(args[:dependencies]) end end class ExecuteDepencencies def self.execute(dependencies) dependencies.each do |dependency| dependency[:module].execute(dependency[:arg]) end end end class UploadFile def self.execute(file) #Do the upload end end class Sendmail def self.execute(email) #Send email end end
Imagine um formulário de cadastro onde o usuário ao submeter os dados já faz o upload do arquivo anexo e envia um e-mail para o mesmo confirmando o cadastro. A responsabilidade de salvar ficou na classe do cliente enquanto fazer o upload e enviar o e-mail ficaram em classes separadas, podendo ser utilizados em outros lugares do sistema em que seja necessário novamente fazer o upload de um arquivo como no cadastro de produtos ou enviar um e-mail como no fechamento de uma compra.
– Open-closed
O princípio de aberto e fechado nos traz o conceito de que devemos sempre deixar nossas classes abertas para serem expandidas, porém fechadas para serem modificadas. Ou seja, você deve isolar a regra para que assim outras classes possam usar ela. Exemplo:
public class User { public void executeRole(String role){ if(role == 'Admin'){ // Do Something } if(role == 'Client'){ // Do Something } } }
No exemplo acima sempre que implementarmos alguma regra nova, por exemplo um Fornecedor (Provider), iremos colocar um if a mais no código e esses ifs tendem ao infinito.
A ideia do princípio é colocar a role em uma interface para retirar esses ifs. Exemplo:
public interface Role { void executeRole(String role); } public class Admin implements Role {} public class Client implements Role {} public class Provider implements Role {}
Desta forma dentro das classes iremos implementar o método executeRole sem nenhum if, apenas executando o que cada um deveria fazer. A ideia nesse caso é que a interface Role seja pouco modificada (o ideal é que ela não seja modificada) e apenas a sua implementação nas classes que irão utilizá-la sejam alteradas conforme a necessidade da classe.
– Liskov substitution
Esse princípio está relacionado a herança e diz que se você tem um objeto B que herda A, em qualquer lugar que você tenha A você pode substituir por B sem precisar alterar o programa. Exemplo:
class Gadget def call(number) true end end class Smartphone < Gadget end class Psp < Gadget def call(number) raise "This gadget do not make calls" end end
Basicamente temos uma classe Gadget e ela possui um método de call (ligar), sendo que Smartphone e PSP são Gadgets, logo herdam ela e trazem consigo o método call. Porém, o PSP não faz ligações, por isso mesmo faremos o método retornar uma exception. Com isso ferimos o princípio de Liskov.
Se tivéssemos uma instância de Gadget e a mesma fosse substituída por Smartphone funcionaria normalmente. O método call, ao invés de ter um retorno booleano, como o método da classe pai fazia, acaba retornando uma exception (claro que poderíamos fazer o método call no PSP, mas queria deixar dessa forma, como um exemplo).
– Interface segregation
O Princípio da Segregação de Interface trata da coesão de interfaces e diz que classes não devem ser forçadas a depender de métodos que não usam. Exemplo:
public interface Activity { void buy(Order order); } public class ShopOwner implements Activity {} public class Client implements Activity {} public class Provider implements Activity {}
Esse exemplo está ferindo o princípio, pois o dono da loja pode comprar do fornecedor e o cliente comprar do dono da loja, mas o fornecedor não compra de ninguém e, nesse caso, você seria obrigado a implementar o método buy no fornecedor, mesmo o método não servindo para ele.
– Dependency inversion
O princípio de inversão de dependência nos diz que SE devemos acoplar alguma classe em outra, devemos fazer esse acoplamento em classes estáveis. De forma mais simples o princípio quer dizer:
- Módulos de alto nível não devem depender de módulos de baixo nível. Ambos devem depender de abstrações.
- Abstrações não devem depender de detalhes. Detalhes devem depender de abstrações.
Exemplo:
public interface ActionAfterBuy { void execute(User user); } public class Cart implements ActionAfterBuy {}
Nesse exemplo o carrinho é um módulo de alto nível e o ActionAfterBuy (Ação depois de comprar) é uma abstração. Perceba que o ActionAfterBuy é uma classe estável, pelo fato de que ela pouco vai ser alterada ao longo do tempo (a ideia de classes estáveis é justamente essa, não alterá-la durante o ciclo de vida do software); além disso, ela não conhece detalhes de sua própria implementação.
Esse é apenas um resumo do que é o SOLID, caso queira se aprofundar no assunto, recomendo fortemente o livro “Orientação a Objetos e SOLID para ninjas” do Maurício Aniche. Outro bom artigo é o “The Principles of OOD”.
Gostou do que falamos? Ficou com alguma dúvida? Deixe nos comentários!