Posicionamento aceitável da raiz da composição usando contêineres de injeção de dependência (DI) e inversão de controle (IoC)

8

Eu li em várias fontes, incluindo o blog 'Ploeh' de Mark Seemann, sobre como o posicionamento apropriado da raiz de composição de um contêiner de IoC é o mais próximo possível do ponto de entrada de um aplicativo.

No mundo .NET, esses aplicativos parecem comumente vistos como projetos da Web, projetos WPF, aplicativos de console, itens com uma interface de usuário típica (leia-se: não projetos de biblioteca).

É realmente contrário a esse sábio conselho colocar a raiz da composição no ponto de entrada de um projeto de biblioteca, quando representa o ponto de entrada lógico de um grupo de projetos de bibliotecas e o cliente de um grupo de projetos como este é o trabalho de outra pessoa , cujo autor não pode ou não adicionará a raiz da composição ao projeto (um projeto de interface do usuário ou ainda outro projeto de biblioteca)?

Estou familiarizado com o Ninject como uma implementação de contêiner de IoC, mas imagino que muitos outros funcionem da mesma maneira, pois podem procurar um módulo contendo todas as configurações de ligação necessárias. Isso significa que eu poderia colocar um módulo de ligação em seu próprio projeto de biblioteca para compilar com a saída do meu projeto de biblioteca principal e, se o cliente quisesse alterar a configuração (um cenário improvável no meu caso), ele poderia soltar uma dll de substituição para substituir o biblioteca com o módulo de ligação.

Isso parece evitar que os clientes mais comuns tenham que lidar com as raízes de injeção e composição de dependência e seria a API mais limpa para o grupo de projetos da biblioteca.

No entanto, isso parece ir contra a sabedoria convencional sobre o assunto. Será que a maioria dos conselhos lá fora pressupõe que o desenvolvedor também tenha alguma coordenação com o desenvolvimento do (s) projeto (s) da interface do usuário, e não o meu caso, no qual estou apenas desenvolvendo bibliotecas para que outros possam usar?

Jeff
fonte

Respostas:

2

Sua abordagem do módulo de encadernação é a melhor solução. Se é improvável que o cliente altere a configuração, o uso do DI é questionável, especificamente para sua biblioteca.

Se você deseja criar uma biblioteca para liberação ao público, provavelmente não deseja que os clientes lidem com a complexidade ou rigidez do sistema de DI específico que você favorece no momento. As bibliotecas são melhores quando são componentes de nível inferior que podem ser usados ​​sem muitas dependências ou treinamento extensivo. Qualquer sistema de DI selecionado muda com o tempo e pode se tornar incompatível com os aplicativos clientes.

Frank Hileman
fonte
2

Uma biblioteca deve ser uma caixa preta com uma API específica de domínio limpa. Não há problema em usar quaisquer ferramentas ou padrões necessários dentro de uma biblioteca, incluindo DI, desde que não vaze para o cliente e o restrinja de alguma maneira.

Se houver necessidade de fornecer extensibilidade ou alterações na configuração da biblioteca, isso ainda poderá ser feito sem o compartilhamento de uma única raiz de composição entre a biblioteca e o cliente.

Se você pode justificar sua biblioteca dependendo de outra biblioteca de terceiros apenas para implementar a DI na sua biblioteca, é um problema diferente, mas lembre-se de que o próprio padrão de DI pode ser implementado sem nenhuma dependência.

astreltsov
fonte
-1

Se você estiver desenvolvendo bibliotecas (não aplicativos) para uso de outras pessoas, ainda deverá implementar a injeção de dependência. Para mim, o termo "injeção de dependência" é apenas um conjunto de práticas que levam a uma melhor separação de preocupações e um acoplamento fraco. É isso que torna sua biblioteca bem estruturada e facilmente testável (na maioria das vezes).

Com relação ao contêiner de IoC, não vejo nenhum mal em contê-lo no projeto da biblioteca. Em algum lugar, você terá uma raiz de composição (um ponto de entrada) em sua biblioteca, portanto, para mim, faz todo o sentido conectar suas dependências na raiz da composição. Como você faz isso é inteiramente sua.

Como exemplo, recentemente usei uma biblioteca para capturar assinaturas e uma biblioteca para digitalizar códigos de barras. Ambos eram bibliotecas de interoperabilidade simples (wrappers COM). Cada biblioteca exigia que eu a inicializasse antes que eu pudesse fazer qualquer coisa. Por exemplo:

var isReady = _signatureCapture.Initialise()
if (isReady)
{
   // Do stuff
}

A biblioteca de leitura de código de barras era muito semelhante:

_scanner.Initialise();
_scanner.OnScan += OnScanHandler;

Eu tenho usado as duas bibliotecas e não tenho idéia se elas usam ou não o contêiner de IoC. No entanto, eu sei que os pontos de entrada na biblioteca estão nos Initialisemétodos. Estou supondo que todas as dependências estejam conectadas no Initialisemétodo, que serve como raiz da composição.

Se você está preocupado com a dependência de um contêiner de IoC de terceiros, pode usar sua própria implementação muito simplificada. Os desenvolvedores geralmente se preocupam com a obsoleta biblioteca ou a substituição de algo melhor, mas, na realidade, ainda não vi um projeto em que a equipe decidiu trocar um contêiner de IoC pelo outro.

CodeART
fonte
4
As bibliotecas devem evitar dependências desnecessárias. Exceto no caso de uma biblioteca muito complexa, mais parecida com um aplicativo, a maioria dos usuários da biblioteca não deseja uma dependência de uma implementação de DI específica.
Frank Hileman
-2

A beleza das bibliotecas externas é que elas devem fazer o que dizem que fariam, enquanto expõem uma interface simples e direta. O quão complexo é realmente fazê-lo, não deve ser da conta do desenvolvedor que o implementa. Se o DI realmente tornar o trabalho do desenvolvedor da API menos complexo, faz 100% de sentido usá-lo, desde que seja usado corretamente e seu módulo seja declarado no ponto de entrada da API. No desenvolvimento de um aplicativo MVC recentemente, abstraí a camada de dados usando um padrão de repositório e ainda mais abstraia colocando uma camada de serviço entre o aplicativo MVC e a camada de repositório, ambos projetos de Biblioteca de Classes. Eu tinha uma interface ICache mapeada para uma classe de cache para o meu contexto de serviço. Inicialmente, eu o implementei usando MVC.Web.Cache, mas em uma iteração posterior, troquei por System.runtime. cache e você pode apenas imaginar o quão grande seria a escala desse Refatorador sem meu DI utilizado. Então meu conselho; "Vá em frente".

abdulgafar jagun
fonte
é difícil ler este post (parede de texto). Você se importaria de editá -lo em uma forma melhor?
gnat