Invertendo a direção das dependências
Autor: Pedro Lamarão | Publicado: 2013-05-16T14:32:00-03:00

Hoje, mais uma vez, resolvi um problema de projeto virando o jogo de cabeça para baixo.

Pretendo refazer este artigo no futuro com uns diagramas mais legais; por enquanto segue apenas o texto para não ficar esquecido.

Um sistema extensível exibe padrões conhecidos; por toda parte vemos sistemas de "plugins".

Imaginando nosso sistema com "plugins", vemos um elemento Central e diversos elementos Adicionais que podem ser inseridos e removidos do sistema à vontade. O elemento Central é um só e há vários Adicionais ligados ao Central.

Extraindo dessa imagem uma imagem conceitual, vemos o tipo Central em relação com o tipo Adicional, onde Adicional tem multiplicidade zero ou mais.

Partindo daqui para uma imagem comportamental, vemos o que acontece quando inserimos o Adicional. Ou tentamos ver. O que acontece? Quem faz o quê?

Vamos supor que estamos criando tudo isso com um servidor de aplicação J2EE. Nesse ambiente, os módulos, quando iniciados, sofrem (1) injeção de dependências em @EJB etc., (2) chamadas a @PostConstruct.

Acompanhando nossa imagem conceitual, onde Central têm Adicionais, buscamos entender como Central pode fazer alguma coisa a cada vez que um Adicional é inserido e iniciado no sistema de módulos.

Se nós temos um sistema com resolução e injeção automática de depenências, declararíamos que o Central tem dependência nos Adicionais, com o objetivo de causar o sistema de módulos injetar Adicionais no Central sempre que forem iniciados, e o inverso quando forem removidos. 

Como acontece isso? O sistema de módulos ativar uma rotina qualquer de Central a cada vez que um Adicional for iniciado -- uma rotina addDependency por exemplo. O oposto aconteceria quando o Adicional é removido. A esperança é que Central receba várias chamadas a addDependency, cada uma das vezes recebendo uma referência ao Adicional que iniciou.

Certo, mas o que acontece quando se faz necessário reiniciar o módulo Central? Todos os módulos Adicionais permanecem iniciados, o que é muito interessante se estes módulos mantém um estado importante; também é um desperdício finalizar e reiniciar módulos à toa. Quando o módulo Central inicia após parar, o sistema de módulos fará o quê? Deve redescobrir a presença de todos os módulos já iniciados? A linguagem usada até agora indica que os Adicionais prévios serão "perdidos", já que addDependency executa apenas quando Adicionais são iniciados.

Se o sistema de módulos concreto usado pelo projeto não fora capaz de fazer isso, qual é a alternativa? Talvez enumerar todos os módulos Adicionais em um arquivo de configuração, atividade que deve ser realizada a cada vez que um Adicional for inserido ou removido do sistema, tal que o módulo Central possa recarregar a configuração e subsequentemente carregar e descarregar Adicionais de acordo.

Isso está ficando um pouco sacal.

Felizmente, para tornar o efeito desejado muitíssimo mais razoável, é preciso apenas inverter a relação de dependência: o Central não depende do Adicional, mas sim o Adicional depende do Central.

Quando um Adicional for iniciado no sistema, este não chamará o Central para fazer algo; o sistema de módulos chamará o próprio Adicional para fazer o que ele acha que deve fazer.


Ao iniciar um Adicional, o sistema de módulos não injetará o Adicional no Central como algum tipo de dependência, mas o contrário: injetará o Central como dependência do Adicional.

Esta relação é simples, já que apenas um Central existe para todos os Adicionais.

Se os Adicionais conhecem o Central através de uma interface útil -- e com certeza deve haver alguma relação entre os módulos, já que todo Adicional deve oferecer um interface útil em contra-partida -- então os Adicionais podem registrar e desregistrar a si mesmos.

Quando o Adicional inicia no sistema, é natural que o sistema cuide de satisfazer suas dependências; se o Central não está lá, o Adicional não pode subir, o que é óbvio; se o Central está lá previamente, deve ser configurado no Adicional. Se o Adicional é reiniciado, o óbvio acontece; se o Central é reiniciado, todos os Adicionais que dependem dele devem ser interrompidos conjuntamente devido a remoção do dependente, o que é razoável, e reiniciados depois quando a dependência se torna satisfeita, o que também é razoável.

O mesmo efeito se obtém no OSGi e em sistema de módulos similares.

É incrível como essa noção é alienígena ao raciocínio do projetista; para o meu raciocínio e o de projetistas conhecidos por mim. É preciso fazer esforço para imaginar um fluxo de trabalho onde o subordinado é quem pilota o gerente, e não o contrário.