Recomendações sobre a integração do contêiner DI / IoC em um aplicativo existente

10

Agora, estou enfrentando a integração de um contêiner de inversão de controle (IoC) em um aplicativo existente, e estou procurando algumas recomendações sobre como isso pode ser realizado com mais facilidade com o objetivo final de reduzir o acoplamento, aumentando assim a testabilidade. Embora eu geralmente não classificasse a maioria das classes como objetos divinos , cada uma tem muitas responsabilidades e dependências ocultas por meio de estática, singletons e falta de interfaces.

Aqui estão alguns antecedentes de alguns dos desafios que precisam ser enfrentados:

  • A injeção de dependência é pouco usada
  • Os métodos estáticos são abundantes - tanto na fábrica quanto nos métodos auxiliares
  • Singletons são bastante prevalentes
  • As interfaces, quando usadas, não são muito granulares
  • Os objetos geralmente recebem dependências desnecessárias por meio de classes base

Nossa intenção é que, na próxima vez que precisarmos fazer alterações em uma área específica, tentemos eliminar dependências que, na realidade, existem, mas que estão ocultas atrás de globais, como singletons e estáticas.

Suponho que isso torne o contêiner de IoC secundário à introdução da injeção de dependência, mas espero que exista um conjunto de práticas e recomendações que possam ser seguidas ou consideradas que nos ajudarão a romper essas dependências.

Kaleb Pederson
fonte
3
Eu tenho que perguntar sobre o motivo de fazer isso agora ... o que está impulsionando essa mudança? Manutenção? Escalabilidade, pois aumentará exponencialmente? Os desenvolvedores estão entediados?
Aaron McIver
11
Acabei de ingressar na empresa e estou tentando introduzir algumas "melhores práticas". Meu principal objetivo é aumentar a testabilidade e reduzir o acoplamento. Podemos usar o DI / IoC com facilidade para novo código e não pretendemos alterar todo o código existente de uma só vez, mas eu gostaria de recomendações sobre como podemos alterar melhor o código existente para melhor na próxima vez que criarmos mudanças nessa área. Até agora, a única coisa que encontrei on-line é a seguinte: code.google.com/p/autofac/wiki/ExistingApplications
Kaleb Pederson
3
A menos que haja um grande número de testes automatizados de unidade / integração; modificar a infraestrutura existente, a menos que exista um problema em prol das práticas recomendadas, está pedindo problemas. Mesmo se o seu conjunto de testes de unidade / integração fosse sólido, eu ainda estava hesitante.
Aaron McIver
11
@ Aaron Espero que não pareça que estamos fazendo isso em prol das melhores práticas. Estamos fazendo alterações porque é difícil e lento trabalhar com o código existente e fazê-lo aos poucos, pois estamos trabalhando nessa área específica. Felizmente, temos um conjunto razoável de testes de integração e alguns testes de unidade para apoiar as alterações.
Kaleb Pederson

Respostas:

8

Para fazer algo assim, você terá que trabalhar em etapas, cada uma delas não é trivial, mas pode ser realizada. Antes de começar, você deve entender que não pode apressar esse processo.

  1. Defina os principais subsistemas do aplicativo e um interfacepara cada um deles. Essa interface deve definir apenas os métodos que outras partes do sistema usarão para conversar com ela. NOTA: pode ser necessário fazer mais de uma passagem.
  2. Forneça uma implementação de wrapper dessa interface que delegue para o código existente. O objetivo deste exercício é evitar a reescrita em massa , mas refatorar o código para usar as novas interfaces - ou seja, reduzir o acoplamento em seu sistema.
  3. Configure o contêiner de IoC para construir o sistema usando as interfaces e implementações que você criou. Nesse estágio, você deseja instanciar o contêiner de IoC para que ele possa abrir o aplicativo. Ou seja, se você estiver em um ambiente de servlet, certifique-se de obter / criar o contêiner no init()método servlet .
  4. Faça o mesmo em cada subsistema novamente, desta vez, quando você refatorar, você está transformando sua implementação de stub na coisa real que, por sua vez, usa interfaces para conversar com seus componentes.
  5. Repita conforme necessário até ter um bom equilíbrio entre o tamanho do componente e a funcionalidade.

Quando terminar, os únicos métodos estáticos que você deve ter em seu sistema são aqueles que são realmente funções - por exemplo, observe a Collectionsclasse ou a Mathclasse. Nenhum método estático deve tentar acessar diretamente outros componentes.

Isso é algo que levará muito tempo, planeje adequadamente. Certifique-se de que, ao executar sua refatoração, esteja ficando mais sólido na sua abordagem de design. No começo, será doloroso. Você está alterando drasticamente o design do seu aplicativo de forma iterativa.

Berin Loritsch
fonte
Boas sugestões. I indicar outras pessoas para as sugestões aqui também ao fazer tais mudanças: code.google.com/p/autofac/wiki/ExistingApplications
Kaleb Pederson
7

Comece a trabalhar efetivamente com o Legacy Code e siga os conselhos dele. Comece construindo ilhas de código coberto e vá gradualmente para um aplicativo mais bem estruturado. Tentar fazer a mudança em massa é uma receita para o desastre.

Michael Brown
fonte
Amo esse livro !!
Martijn Verburg
Boa recomendação. Embora eu tenha lido metade dela anos atrás, imagino que eu obteria muito mais com isso agora que estou com a cintura na situação.
Kaleb Pederson
É engraçado, a resposta selecionada basicamente resume o livro;) É claro que Feathers entra em muito mais detalhes.
Michael Brown
5

A principal razão para a introdução da IoC é a dissociação de módulos. O problema com Java especialmente é a ligação forte extraordinária que o newoperador fornece, combinada com isso significa que o código de chamada sabe exatamente qual módulo ele usará.

As fábricas foram introduzidas para mover esse conhecimento para um local central, mas, no final, você ainda conecta os módulos usando new/ singleton, que mantém o vínculo rígido, ou lê um arquivo de configuração e usa reflexão / Class.forName, que é frágil ao refatorar .

Se você não tiver o destino modularizado, a IoC não fornecerá nada.

A introdução de testes de unidade provavelmente mudará isso, pois você precisará zombar dos módulos que não estão sendo testados, e a maneira mais fácil de lidar com isso, bem como os módulos de produção reais com o mesmo código, é usar uma estrutura de IoC para injetar o apropriado módulos.


fonte