Inversão de Controle
Inversão de controle (Inversion of Control, ou IoC) é uma técnica muito utilizada atualmente e com a qual desenvolvedores freqüentemente têm que lidar, por isso é importante compreender como ela funciona.
A inversão de controle consiste na mudança do fluxo de controle de um programa, onde ao invés do programador determinar quando um procedimento será executado ele apenas determina qual procedimento deve ser executado para um determinado evento.
Motivações
As principais motivações são: o reaproveitamento do código que contém o fluxo de controle, reaproveitamento do design e reaproveitamento do comportamento da aplicação. Isso está alinhado às boas práticas de programação orientada a objetos, pois assim é possível atingir um baixo acoplamento na arquitetura e um alto nível de testabilidade.
Exemplo
Aqui temos o código de um pequeno questionário em Ruby (adaptado do artigo de Martin Fowler):
#ruby puts 'Qual é o seu nome?' nome = gets process_nome(nome) puts 'Qual o seu pedido?' pedido = gets process_pedido(pedido)
No fragmento de código acima o fluxo de controle é determinado pelo próprio programa. Ele decide quando deve realizar as perguntas, ler as respostas e processar os resultados. Se estivéssemos usando um sistema de janelas teríamos então que configurar a janela a ser utilizada da seguinte maneira:
require 'tk'
root = TkRoot.new()
nome_label = TkLabel.new() {
text "Qual é o seu nome?"
}
nome_label.pack
nome = TkEntry.new(root).pack
nome.bind("FocusOut") {
process_nome(nome)
}
pedido_label = TkLabel.new() {
text "Qual o seu pedido?"
}
pedido_label.pack
pedido = TkEntry.new(root).pack
pedido.bind("FocusOut") {
process_pedido(pedido)
}
Tk.mainloop()
Nesse caso, o fluxo de controle foi entregue ao sistema de janelas (através do comando Tk.mainloop). É o sistema de janelas agora quem decide quando vai realizar as perguntas, com base nas configurações feitas durante a criação do formulário. Em relação ao primeiro exemplo o fluxo de controle está invertido, pois é o framework que usa o código que fizemos ao invés do código que fizemos usar o framework.
Frameworks
O uso de IoC normalmente está associado a aspectos arquiteturais do sistema pois, como pudemos observar no exemplo, ele determina o fluxo do código. Uma arquitetura que mantém o fluxo de controle generalizado (através de abstrações, como o uso de interfaces, por exemplo), dá origem ao que chamamos de frameworks. Eles funcionam como estruturas pré-fabricadas, prontas para receber uma implementação específica para um determinado comportamento.
Frameworks populares hoje em dia, como o Rails, surgiram dessa forma. Ao identificar o potencial de uso de sua arquitetura para as mais variadas aplicações, criou-se uma abstração em torno da mesma que permitisse com que a sua funcionalidade primária fosse extendida. Inversão de controle é uma característica comum aos frameworks, sendo na verdade freqüentemente aceita como uma das características que os definem.
Nas palavras de Ralph Johnson e Brian Foote: “O framework geralmente assume o papel do programa principal de coordenar e seqüenciar a atividade da aplicação. Essa inversão de controle dá aos frameworks o poder de servir como se fossem esqueletos de aplicação extensíveis.”
Apesar de ser mais comum nos depararmos com IoC em frameworks, essa técnica também é utilizada em outras situações, como no padrão Template Method, por exemplo.
“Don’t call us, we’ll call you”
Inversão de controle é também conhecida como o “Princípio de Hollywood”, em uma analogia com a forma como são feitos os negócios na terra do cinema. Assim como são os estúdios que chamam o ator para o filme, é o framework que chama a implementação feita pelo desenvolvedor, e não o contrário.
Isso é o que faz um framework essencialmente diferente de uma biblioteca. Uma biblioteca funciona como um conjunto de funções que podem ser usadas. Ao usar uma dessas funções, o controle volta para quem a está usando. Já o framework incorpora um comportamento, extensível através da implementação de suas interfaces, ao criar subclasses ou plugar módulos. Depois é o próprio código do framework que usa essa implementação específica que lhe foi entregue.
Injeção de dependência
Normalmente lembramos de IoC quando estamos usando algum dos conhecidos containers para injeção de dependência (Dependency Injection, ou DI). É importante ressaltar que esses containers nada mais são do que frameworks, onde DI é uma implementação que faz uso de IoC. Em outras palavras, IoC é a estratégia enquanto que DI é a tática usada pelo container.
Uma confusão comum ao tratar desse tema é achar que IoC e DI são sinônimos, enquanto na verdade seria mais correto dizer que o último é uma especialização do primeiro. A especialização está no aspecto do controle que está sendo invertido: DI faz com que o controle de construção dos objetos usados na aplicação passe para o container, de forma que o programador não precise explicitamente instanciá-los.
Nas palavras do próprio Martin Fowler, “dizer que esses containers são especiais porque eles usam inversão de controle é como dizer que meu carro é especial porque ele tem rodas”. Exemplos de containers de DI conhecidos são o Spring, Castle Windsor, PicoContainer, Guice e Unity (a ser discutido em um próximo post nesse blog).
Referências
- Inversion of Control (Martin Fowler)
- Inversion of Control Containers and the Dependency Injection pattern
- Inversion of Control (Wikipedia)
Assuntos Relacionados
- Princípio da Inversão de Dependência
- Injeção de Dependência
- Testes de Software
- Padrões de Projeto
- Design Principles and Design Patterns
Discussões
- Diferença entre “Injeção de dependência” e “Inversão de controle”
- Inversion of Control != Dependency Injection
Tags: arquitetura, Design, padrões de projeto, Testes