O acoplamento fraco entre unidades distintas de um sistema já faz parte do bom senso de um bom desenvolvedor. Sabemos que é muito mais fácil lidar com o conhecimento pontual de uma correção do que a necessidade de conhecimento de uma vasta hierarquia de relacionamento entre unidades e, uma das técnicas que promove o tão procurado sistema flexível de baixo acoplamento é a inversão de controle.

A inversão de controle é a técnica que inverte o paradigma de uma programação tradicional. Com a aplicação dessa técnica, o que antes era feito de forma direta é agora delegado para uma infraestrutura dedicada que muitas vezes é também conhecida como container. Em geral, e de forma menos abstrata, o que temos é que a instanciação das dependências de uma determinada classe se dará fora dela. Vejamos um exemplo prático de como isso deve acontecer:

Imagine a sintonização de um canal através de uma antena comum conforme o exemplo abaixo:

public class Televisao {

  public void sintonizar canal(final int canal){
    Sintonizador sintonizador = new SintonizadorPorAntena();
    sintonizador.sintonizar(canal) ;
  }

}

Se a primeira vista não foi possível observar qualquer problema nesse design então suponha que agora você queira que a sintonização da sua TV seja realizada por um aparelho a cabo e não mais por antena. Acontece que, pelo exemplo anterior, será necessário realizar uma manutenção na sua TV que a principio não tinha nenhum problema, ou seja, uma unidade externa absolutamente independente do seu aparelho exigiu uma manutenção nele. Sendo um um pouco mais claro, tal mudança exigiria algo como a seguinte manutenção:

public class Televisao {

  public void sintonizar canal(final int canal){
    Sintonizador sintonizador = new SintonizadorPorCabo();
    sintonizador.sintonizar(canal) ;
  }

}

Em outras palavras, toda vez que houver a necessidade de se alterar o sintonizador de uma televisão, teremos que realizar uma manutenção na classe televisão o que deixa evidente um alto grau de acoplamento entre unidades. Um pouco mais a fundo, outro desvio dessa classe é que ela fere um principio SOLID pois não respeita o “principio aberto fechado” (open close principle) pois a medida que um sintonizador novo surge somos obrigados a refazer a classe televisão pois a lógica que trata o sintonizador está chumbada nela.

Entidades de software (classes, módulos, funções, etc) devem ser abertas para a extensão, mas fechadas para modificação – Meyer, Bertrand (1988)

Para resolver esse problema realizaremos uma inversão de controle que prontamente removerá a lógica de instanciação do sintonizador na classe televisão. Para isso podemos usar duas abordagens: obter a instância de sintonizador a partir do uso de um ServiceLocator ou então, realizando uma injeção de dependência. Optaremos neste artigo por uma injeção de dependência do tipo “constructor injection” o que significa que injetaremos a dependência chamando um método construtor.

public class Televisao {

  public final Sintonizador sintonizador;

public class Televisao(final Sintonizador sintonizador){
    this.sintonizador = sintonizador;
  }

public void sintonizar canal(final int canal){
    sintonizador.sintonizar(canal) ;
  }

}

Muito melhor agora! Já não há mais a necessidade de manutenção da classe Televisão pelo simples motivo de se querer atender um novo sintonizador. Afinal, toda vez que surgir um novo sintonizador e houver a necessidade de variá-lo bastará instanciar a televisão com o novo sintonizador, o que ficará a cargo do seu framework de IoC. Outro grande ganho, além da diminuição da carga de manutenção sobre a classe Televisão, se deve ao fato de que uma classe menos complexa possui menor combinação de cenários que deve atender e, justamente por essa característica, tende a ser menos propícia a falhas. Indo um pouco mais além, no que diz respeito aos testes unitários, um objeto que faz o uso de inversão de controle é amplamente mais testável do que um que não o faz! É muito mais fácil simular comportamento da colaboração entre classes a partir do uso de mocks do que sobre objetos concretos.

Por fim, basta agora encontrar um framework que suporta IoC e que melhor se adapte às necessidades do seu sistema como por exemplo o Spring que é amplamente usado no mercado e se dá muito bem com injeção por setters, o Picocontainer que trabalha bem com injeção por construtores assim como o Guice (“juice“) do Google e o VRaptor.

Artigos relacionados

Boas práticas de programação com a aplicação do princípio da responsabilidade única

Referências

OODesign – Open Close Principle

Anúncios

Publicado por Felipe Pierin

Bacharel em Sistemas de Informação pela Universidade de São Paulo, desenvolvedor Java para a Web e especialista em desenvolvimento orientado a testes e boas práticas de programação. Escreve artigos para revistas especializadas e publica periodicamente dicas de programação no seu blog pessoal.

Se Junte à Conversa

3 comentários

  1. Felipe, tenho dúvidas:
    Nesse exemplo de injeção, para onde foi a lógica de instanciação do sintonizador, já quem não é em Televisor onde devo implementa-la?
    Ainda não enxerguei uma necessidade prática para a injeção no tocante a quando você diz: “pois a medida que um sintonizador novo surge somos obrigados a refazer a classe televisão pois a lógica que trata o sintonizador está chumbada nela”, penso que de qualquer forma vou ter que dar manutenção na classe Televisor.

    1. Boa noite Guilherme! Obrigado pelo feedback! Vamos lá…

      1. Com relação a primeira dúvida, você precisa ter em mente que terá um framework atuando como container e que se responsibilizará por, de alguma forma automática, auto injetar suas dependências. No caso, quem faria isso seria por exemplo, o Spring quando você configura o xml do application-context, ou por meio de anotações como o Guice.

      2. A necessidade da inversão de controle é simples ! Você tem o código chumbado la certo? Então quando surgir um SintonizadorPorCabo você vai criar uma lógica para escolher entre um SintonizadorPorAntena ou um SintonizadorPorCabo na classe televisão. Imagine que para piorar ainda mais surja uma classe “SintonizadorPorParabolica”, agora você vai ter que escolher entre 3 classes. Mas onde você vai colocar essa regra? Sem inversão de controle você coloca na classe televisão. O que é, em geral, muito ruim. Esse tipo de escolha lhe prejudicará com testes unitários já que você não vai conseguir gerar mocks das classes pois não estão sendo injetadas. Outro problema é que aumenta a quantidade de cenários que a classe televisão abrange. Isso a torna mais complexa do que deveria e aumenta o risco de, ao acrescentar mais funcionalidade, estragar uma pré-existente.

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair /  Alterar )

Foto do Google

Você está comentando utilizando sua conta Google. Sair /  Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair /  Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair /  Alterar )

Conectando a %s

%d blogueiros gostam disto: