S.O.L.I.D no Android

Nesse artigo vou explicar de maneira objetiva como utilizar o acrônimo SOLID no seu projeto Android.

O SOLID possui 5 principios de programação orientada a objetos:

# S :SRP —Single responsibility principle : Principio da Responsabilidade Única

Single responsibility principle

# O : OCP —Open/closed principle : Princípio Aberto-Fechado

# L : LSP — Liskov substitution principle : Princípio da Substituição de Liskov

# I :ISP — Interface segregation principle : Princípio da Segregação da Interface

# D :DIP —Dependency inversion principle : Princípio da inversão da dependência

SRP: Principio da Responsabilidade Única

Uma classe deve ter um, e somente um, motivo para mudar.

Vamos utilizar o preenchimento de uma lista como exemplo.

Muitas vezes colocamos regra de negócio no método onBindViewHolder do nosso Adapter, quando na verdade ele é somente responsável pelo preenchimento dos items do nosso RecyclerView.

Vejamos um exemplo de violação desse primeiro princípio na prática:

A violação ocorre no método onBindViewHolder, onde estamos formatando a string "date" para setar no nosso holder.date da maneira correta.

Esse método é responsável por popular os nossos items do RecyclerView, e não executar regras de negócio e formatação de dados.

Para corrigirmos o nosso código e manter esse princípio temos que retirar a formatação dos dados desse método, como podemos ver no exemplo:

Como podemos ver acabamos com a violação retirando a formatação do campo "date" para o método Util.formateDate(intervalDataBase.date) que faz a o que precisamos sem violar o princípio.

Extra:

Um outro caso onde podemos verificar a violação desse princípio é quando utilizamos o padrão MVP(Model View Presenter) no nosso código e colocamos regras de negócio na nossa classe que implementa a nossa View. Que é somente responsável por mostrar os dados na tela e não executara qualquer regra de negócio. Pois isso é responsabilidade do Presenter.

Caso de dúvidas sobre MVP acesse:

http://www.tinmegali.com/pt/model-view-presenter-mvp-no-android-introducao/

OCP : Princípio Aberto-Fechado

Você deve ser capaz de estender um comportamento de uma classe, sem modificá-la.

Como o título ja nos diz, não podemos modicar o comportamento de um classe e sim estender a mesma. Vamos utilizar um exemplo de cálculo de área de objetos diferentes.

Neste primero exemplo vamos violar esse princípio:

Caso tenhamos a necessidade de adicionar novos objetos para calcular a área, temos que adicionar um bloco de código a nossa classe AreaFactory, violando o princípio, já que não podemos modificar a nossa classe para cada objeto novo que adicionarmos.

Para solucionar esse problema que acabamos de criar temos que retirar essa lógica de adicionar a cada novo objeto um novo bloco de código, que deixe a nossa classe cada vez maior e confusa.

Segue a solução:

Criamos uma interface chamada Shape que possui o método getArea() que é implementado em todos os nosso objetos (Circle e Rectangle).

Podemos adicionar novos objetos sem ter a necessidade de modificar a nossa AreaFactory para calcular o total da soma das áreas, simplificando o nosso código e não modificando a nossa classe principal.

Extra:

Um exemplo mais prático no Android desse princípio é a capacidade que temos de criar customViews sem modificar as as Views de origem.

Nesse caso só extendemos a classe EditText para criar a nossa própia, onde nesse exemplo já podemos setar a font direto no arquivo xml.

LSP : Princípio da Substituição de Liskov

As classes derivadas devem ser substituíveis por suas classes base.

No princípio da substituição de Liskov as classes filhas nunca devem romper com as definições de tipos da classe pai.

No exemplo abaixo temos a violação, onde temos duas classes que implementam a classe pai Car que são respectivamente Ferrari e Tesla.

No método letStartEngine() passamos o objeto Car para iniciar o carro, porém se o carro for Tesla e estiver descarregado a chamada car.statEngine(), não ira funcionar, ocasionando um bug no nosso código.

Uma possível solução seria dentro do método letStartEngine verificar se nosso car é um Tesla.

Em seguida chamamos o método car.TurnOnCar(), para ligá-lo caso ele tenha bateria.

Fazendo isso acabamos violando o OCP, pois estamos modificando nossa classe para se adaptar com os possíveis objetos, ou seja esta solução não serve pois devemos manter intacta à nossa classe que executa nossa tarefa de ligar o carro.

Fazer o cast para verificar qual é a classe é um code smell também.

A solução para esse caso é colocar dentro da nossa classe Tesla a verificação se o carro está sem bateria, para depois liga-lo. Assim as duas classes filhas de Car retornam a mesma solução para o método statEngine().

ISP: Princípio da Segregação da Interface

Muitas interfaces específicas são melhores do que uma interface única.

Nesse caso vamos falar da implementação de varias interfaces que nem se quer utilizamos.

No caso da classe abaixo OnClickListener, na sua utilização temos que implementar os 3 métodos no nosso click sendo que não vamos usar todos os métodos.

Obs: A interface OnClickListener na prática não nos obriga a implementar os 3 métodos, mas para o exemplificar criamos a nossa própria onde nos obriga, para fins de estudo.

A violação ocorre no código abaixo onde precisamos somente do método onClick() mas como utilizamos a nossa interface OnClickListener, somos obrigados a implemtar onLongClick e onTouch sem nem mesmo utiliza-los.

A solução para manter esse príncipio é separar cada método da nossa interface em diferentes interfaces, assim só vamos implementar aquilo que realmente vamos utilizar, evitando o desperdício de código e mantendo o príncipio na nossa implementação.

DIP : Princípio da inversão da dependência

Dependa de uma abstração e não de uma implementação

No último principio do SOLID, e não menos importante, temos a inversão de dependência como padrão, ou seja classes de alto nível nao podem depender de classes de baixo nível e sim ambas dependerem de uma abstração.

Vamos ver o exemplo da violação:

O problema com essa implementação é que a classe Engineer depende da classe Program.

Engineer é a nossa classe de alto nível e não deve adaptar-se a mudança de outras classes para continuar funcionando ou seja não depender de ninguém.

Program é de baixo nível pois deve se adaptar a classe de alto nível.

Caso seja adicionado um novo SuperProgram teriamos que mudar a lógica da nossa classe de alto nível Engineer e considerando que ela possa ser complexa, a mudança poderia afetar o nosso código e causar outros problemas. Para evitar que isso aconteça invertemos as dependência das classes como no exemplo a seguir:

No código acima adicionamos uma abstração que é a interface IProgram que nossos objetos a implementam, deixando a relação entre os objetos e a nossa classe de alto nível totalmente destinta.

Para retirar a dependência da classe Enginner, não foi preciso modificar nada no código pois nossa abstração resolveu esse problema.

Conclusão:

Sempre que podermos utilizar SOLID, estaremos ganhando tempo no futuro, para dar manutenção e realizar testes. Além de ser uma ótima oportunidade de aplicar algo novo no seu código que te tornara um programador melhor.

Por que escrevi esse artigo?

A ideia surgiu no lugar onde trabalho, para apresentar algo “novo” para o time de mobile e também para que eu podesse melhorar meu código utilizando SOLID.

Então recomendo que estudem coisas novas, escrevam sobre elas e compartilhem.

Bibliografia: