Conheça a arte do SOLID

em Desenvolvimento.

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!

Você também pode gostar