Fernando Nagase

Engenheiro de Software

A história da Inversão de Controle em 10 minutos

Uma retrospectiva desde o "princípio de Hollywood" até a injeção de dependência

Publicado em 20 out. de 2023 · Escrito por Fernando

Entre o final da década de 1970 e o início da década de 1980, uma equipe de programadores do Xerox PARC (Palo Alto Research Center) trabalhava na criação de um ambiente de ferramentas integradas com o objetivo de facilitar o desenvolvimento e manutenção de software com a linguagem Mesa.

Um dos componentes centrais do XDE (Xerox Development Environment), como foi denominado o projeto, era o Tajo, que, resumidamente, oferecia o arcabouço necessário para implementar e executar as ferramentas em questão.

Diferentemente de outros ambientes de desenvolvimento daquela época, que executavam uma única tarefa por vez, com o Tajo, era possível interagir com múltiplas ferramentas simultaneamente, pois ele representava cada uma delas como uma janela em uma interface gráfica de usuário.

Para tanto, uma característica especialmente interessante no contexto deste artigo foi adotada no design do Tajo: as ferramentas agregadas “sob o seu guarda-chuva” deveriam funcionar de forma passiva e não podiam iniciar procedimentos por conta própria.

Em outras palavras, o Tajo funcionava de acordo com um sistema de notificações, em que cada ferramenta entraria em ação — por meio de procedimentos de resposta previamente configurados pelo seu desenvolvedor — apenas quando notificadas pelo Tajo de que o usuário realizou alguma interação (com mouse ou teclado, por exemplo) com uma janela pertencente a ela.

Essa característica foi sintetizada pelo nome lei de Hollywood ou princípio de Hollywood na especificação funcional do Tajo, aparentemente, em referência à forma como atores e atrizes iniciantes adentravam no mundo de Hollywood. A explicação a seguir é feita por Alexandre Aquiles no livro Desbravando SOLID (2022):

Há milhares de atores e atrizes tentando a sorte em Hollywood. E todo filme tem uma pessoa responsável por definir quem vai interpretar cada papel. Imagine se os milhares de atores e atrizes ligassem à procura de uma oportunidade. A pessoa responsável pelo elenco não sairia do telefone!

Por isso, em Hollywood, a pessoa responsável pelo elenco obtém uma lista de atores e atrizes com seus telefones e as características de cada um. E essa pessoa comumente diz: “Não me ligue, deixa que eu te ligo”. Esse é o Hollywood Principle.

Ou seja, da mesma forma que a pessoa responsável pelo elenco de um filme se encarregaria de ligar para um ator ou atriz caso houvesse um papel disponível, o Tajo tinha o papel de notificar cada ferramenta quando fosse o momento certo de agir.

No ano de 2004, Martin Fowler publicou um exemplo em seu site que parece compatível com o princípio de Hollywood e nos ajuda a entendê-lo melhor:

As primeiras interfaces de usuário eram controladas pelo programa da aplicação. O que você tinha na interface de um programa era uma sequência de comandos como “Insira seu nome”, “Insira seu endereço” etc., e ele conduziria esses prompts para coletar uma resposta para cada um deles. Tratando-se de UIs gráficas (ou mesmo as baseadas em telas), o framework de UI é que conteria o loop principal da aplicação, e o seu programa apenas forneceria event handlers para cada campo (de um formulário) na tela. O controle principal do programa foi invertido, afastado de você em direção ao framework.

(Traduzido e adaptado por Fernando Nagase)

Desenvolvendo software ao contrário

Você notou como, no exemplo, Fowler utilizou a expressão “o controle do programa foi invertido”? Acontece que o comportamento promovido pelo princípio de Hollywood é exatamente o que seria chamado, posteriormente, de inversão de controle. Esse era, inclusive, o tema abordado por Fowler no artigo em questão.

Até o momento, discutimos apenas sobre o contexto de interfaces gráficas de usuário, mas a inversão de controle é um princípio ainda mais amplo, que se aplica à matéria de frameworks de software como um todo, conforme seria abordado por alguns autores anos após a concepção do princípio de Hollywood.

De acordo com Fowler (2004), o termo inversão de controle teria aparecido pela primeira vez no artigo Designing Reusable Classes, publicado por Ralph Johnson e Brian Foote em 1988, em que os autores discutiam sobre o projeto (design) de classes reutilizáveis em programação orientada a objetos com a linguagem Smalltalk.

Nesse artigo, Johnson e Foote descrevem o fato de que, em frameworks de software, é comum que os métodos implementados pelos usuários do framework para suprir uma aplicação sejam executados e coordenados pelo próprio framework, em vez de serem chamados explicitamente pelo código do usuário.

Essa característica, referenciada pelo artigo de forma tangecial como uma inversão de controle, foi considerada importante pelos autores por permitir aos frameworks de software funcionarem como “esqueletos extensíveis”, que podem ser adaptados por métodos fornecidos pelos seus consumidores para atender diferentes aplicações.

Em uma publicação de 1996, John Vlissides também abordou o tema e afirmou que, por meio do princípio de Hollywood, frameworks de software são capazes de capturar aspectos invariáveis de arquitetura e implementação, enquanto delegam as partes variáveis para o código específico de cada aplicação.

Essa mesma inversão de controle foi tratada por Michael Mattson, também em 1996 (conforme citado por Stefano Mazzocchi em 2004), como a principal diferença entre frameworks orientados a objetos (que chamam, eles mesmos, o código de uma aplicação) em relação a bibliotecas de classes (que são chamadas pelas aplicações).

Mudança de paradigma

Até o final da década de 1990, a expressão “inversão de controle” aparecia frequentemente ao lado de “princípio de Hollywood”, mas, diferentemente da forma como a aplicamos atualmente, não parecia ser considerada um termo por si só.

Um evento-chave para essa mudança de interpretação parece ter ocorrido em 1998, quando Stefano Mazzocchi apresentou o conceito da inversão de controle à comunidade do Apache Avalon, um projeto cujo objetivo era prover um framework para o desenvolvimento de aplicações server-side na linguagem Java.

Um dos princípios adotados pelo Avalon era a programação orientada a componentes (COP), que promove a modularização de aplicações por meio do conceito de componente, isso é, uma porção delimitada de código exposta sob uma interface padronizada e que resolve uma responsabilidade bem definida.

Em outras palavras, para lidar com cada responsabilidade (como acesso ao banco de dados, caching, autorização etc.) em uma aplicação desenvolvida com o Avalon, era possível escolher diferentes implementações de componentes, que, por funcionarem sob uma interface comum, poderiam ser utilizadas de forma intercambiável.

A IoC era utilizada pelo Avalon no contexto do gerenciamento de dependências entre componentes, pois encorajava que um componente não configurasse suas dependências por conta própria, mas as recebesse já configuradas dos trechos de código onde fossem utilizados, nesse caso, chamados de contêineres do componente.

Figura 1 - Sem a IoC (à esquerda), o componente A instancia suas próprias dependências. Com a IoC (à direita), ele as recebe de seu “contêiner” (o método main)

Ocorre que, em grandes projetos de software, que reúnem um número elevado de componentes, tornaria-se difícil gerenciar manualmente o ciclo de vida de todos eles, já que o próprio desenvolvedor precisaria coordenar as suas rotinas de inicialização e destruição. O exemplo a seguir foi retirado do documento “Developing With Avalon” e demonstra a verbosidade desse tipo de gerenciamento.

class ContainerComponent implements Component, Initializable, Disposable
{
    DocumentRepository docs = new DatabaseDocumentRepository();
    GuardianComponent guard = new DocumentGuardianComponent();
    DefaultComponentManager manager = new DefaultComponentManager();

    public void initialize()
        throws Exception
    {
        Logger docLogger = new LogKitLogger( Hierarchy.defaultHierarchy()
        .getLoggerFor( "document" ) );

        this.docs.enableLogging( docLogger.childLogger( "repository" ) );
        this.guard.enableLogging( docLogger.childLogger( "security" ) );

        DefaultConfiguration pool = new DefaultConfiguration("dbpool");
        pool.setValue("main-pool");
        DefaultConfiguration conf = new DefaultConfiguration("");
        conf.addChild(pool);

        this.manager.addComponent( DocumentRepository.ROLE, this.docs );
        this.manager.addComponent( GuardianComponent.ROLE, this.guard );
        this.docs.compose( this.manager );
        this.guard.compose( this.manager );

        this.docs.configure(conf);

        this.guard.initialize();
        this.docs.initialize();
    }

    public void dispose()
    {
        this.docs.dispose();
        this.guard.dispose();
    }
}

Com isso, uma solução proposta pelo Avalon foi a criação de um “contêiner genérico” (Excalibur Component Manager) para automatizar o controle sobre cada componente, que poderia ser configurado por meio de arquivos XML. Desse modo, os componentes poderiam ser facilmente acessados por meio do ECM, removendo-se a necessidade de se preocupar com seus ciclos de vida.

Foi exatamente esse contexto de gerenciamento de dependências que, no início da década de 2000, influenciou o surgimento de outros frameworks, como o PicoContainer e Spring Framework, chamados de “contêineres baseados em IoC”, que ofereciam funcionalidades semelhantes ao Avalon.

Desde então, o termo “inversão de controle” passou a ser cada vez mais utilizado de forma “standalone” no contexto de gerenciamento de dependências e estabeleceu uma nova tendência.

Injeção de dependência

Em 2004, Martin Fowler publicou um artigo em que discutia sobre o surgimento de uma nova onda de “lightweight containers” e sobre como eles se relacionavam com o princípio da inversão de controle. Um trecho desse artigo nos é especialmente interessante:

A Inversão de controle é uma característica comum entre frameworks, logo, dizer que esses “lightweight containers” são especiais por usarem inversão de controle é como dizer que meu carro é especial por ter rodas.

A pergunta é: “qual aspecto do controle eles estão invertendo?”

(Traduzido e adaptado por Fernando Nagase)

Ou seja, indo ao encontro da definição de Foote e Johnson (1988) sobre como a IoC está inerentemente ligada ao próprio conceito de framework de software, Fowler julgou inadequado o uso desse termo para designar o padrão de projeto implementado pelos contêineres analisados, por ser muito genérico.

Antes de publicar o artigo, entretanto, Fowler já havia demonstrado um rascunho para outros programadores envolvidos com o assunto, entre os quais Rod Johnson (criador do Spring Framework) e Paul Hammant (co-líder do PicoContainer), com quem discutiu uma nova nomenclatura, chegando ao termo “Injeção de Dependência”.

Desde então, a escolha entre um termo ou outro parece ter se tornado confusa, principalmente pelo fato de algumas pessoas terem passado a considerá-los sinônimos, o que foi refutado por Mazzocchi alguns dias após a publicação original de Fowler.

Com base nas posições de Mazzocchi (2004) e Fowler (2005), assim como a defendida em um artigo no site do PicoContainer sobre a história da IoC, considero seguro tratar a inversão de controle como um princípio amplo (conforme abordado por Johnson e Foote, Vlissides e Mattson).

Por outro lado, a injeção de dependência se trata de uma entre diversas formas de se atingir a inversão de controle, tendo seu foco voltado para a coordenação de dependências entre objetos (conforme implementado pelo PicoContainer, Spring Framework e demais contêineres do tipo).

Conclusão

As primeiras discussões sobre a inversão de controle foram levantadas no contexto de frameworks de software e a tratavam como um princípio amplo, preocupado com a transferência de aspectos reutilizáveis entre aplicações das mãos dos desenvolvedores para as mãos de frameworks.

No entanto, a grande popularização do termo pelos chamados “contêineres baseados em IoC” fez com que o conceito da inversão de controle se confundisse com o que hoje chamamos de “injeção de dependência”, isso é, uma forma específica de IoC voltada para o gerenciamento de dependências entre os componentes de uma aplicação.

Referência

  1. Mesa — Software Preservation Group
  2. Tajo_Functional_Specification_Version_6.0_Oct1980.pdf (bitsavers.org)
  3. Xerox Mesa Programming - XDE (archive.org)
  4. 610E00130_XDE_Concepts_and_Principles_Sep85.pdf (bitsavers.org)
  5. Livro Desbravando SOLID - Casa do Código (casadocodigo.com.br)
  6. Inversion of Control Containers and the Dependency Injection pattern (martinfowler.com)
  7. InversionOfControl (martinfowler.com)
  8. Designing Reusable Classes (laputan.org)
  9. “Pattern Hatching” column for February ‘96 issue John Vlissides (archive.org)
  10. Stefano’s Linotype ~ On Inversion of Control (archive.org)
  11. Avalon Framework - Guide - Inversion of Control (archive.org)
  12. Avalon Framework - Guide - What is COP? (archive.org)
  13. Excalibur - ECM (apache.org)
  14. PicoContainer - (archive.org)
  15. The Spring Framework - A Lightweight Container (archive.org)
  16. PicoContainer - Inversion of Control History
  17. Technical Deficit : Inversion of Control (n327.blogspot.com)
  18. Inversion of control - Wikipedia
  19. Stefano’s Linotype ~ Origin of the Hollywood Principle (archive.org)
  20. 610E00201_XDE_User_Guide_Oct88.pdf (bitsavers.org)
  21. Inversion of Control Rocks @ JDJ (archive.org)
  22. developing-with-avalon.pdf (psu.ac.th)

Atribuições

Fotografia da capa por Cedric Letsch em Unsplash