Estou brincando com injeção de dependência, mas não tenho certeza se estou fazendo certo. Especialmente, não tenho certeza de qual deve ser a maneira correta de criar classes com dependências injetadas.
Digamos que eu tenha uma classe A que crie a classe B. A classe B depende da classe C e a classe C depende da classe D. Quem deve ser responsável pela criação da classe D?
Pode ser a classe A. No entanto, em um sistema grande, a classe A pode acabar criando e montando um número muito grande de objetos.
Uma classe de construtor separada que criará D, C e B. A usará essa classe de construtor.
Alguma outra opção.
Além disso, eu li muito sobre contêineres DI. No entanto, parece que não há grandes estruturas para C ++. Além disso, se eu entendi direito, o DI pode ser executado bem, mesmo sem contêineres. Estou correcto?
fonte
Respostas:
Você está pulando passos. Considere um conjunto de convenções otimizadas para acoplamento flexível e segurança de exceção. As regras são assim:
R1: se A contém um B, então o construtor de A recebe um B totalmente construído (isto é, não "dependências de construção de B"). Da mesma forma, se a construção de B exigir um C, ele receberá um C, não as dependências de C.
R2: se uma cadeia completa de objetos é necessária para construir um objeto, a construção encadeada é extraída / automatizada dentro de uma fábrica (função ou classe).
Código (chamadas std :: move omitidas por simplicidade):
Nesse sistema, "quem cria D" é irrelevante, porque quando você chama make_b, precisa de um C, não de um D.
Código do cliente:
Aqui, D é criado pelo código do cliente. Naturalmente, se esse código for repetido mais de uma vez, você poderá extraí-lo em uma função (consulte R2 acima):
Existe uma tendência natural para pular a definição de make_b (ignorar R1 ) e escrever o código diretamente assim:
Nesse caso, você tem os seguintes problemas:
você tem código monolítico; Se você encontrar uma situação no código do cliente em que precisa criar um B a partir de um C existente, não poderá usar o make_b. Você precisará escrever uma nova fábrica ou a definição de make_b e todo o código do cliente usando o antigo make_b.
Sua visão das dependências é confusa quando você olha para a fonte: Agora, olhando para a fonte, você pensa que precisa de uma instância D, quando na verdade você pode apenas precisar de um C.
Exemplo:
struct A { B make_b(C c); }
aumentará muito o acoplamento: agora A precisa conhecer as definições de B e C (em vez de apenas C). Você também tem restrições em qualquer código de cliente usando A, B, C e D, impostas ao seu projeto porque pulou uma etapa na definição de um método de fábrica ( R1 ).TLDR: Em resumo, não passe a dependência mais externa para uma fábrica, mas as mais próximas. Isso torna seu código robusto, facilmente alterável e transforma a pergunta que você fez ("quem cria D") em uma pergunta irrelevante para a implementação do make_b (porque o make_b não recebe mais um D, mas uma dependência mais imediata - C - e isso é injetado como um parâmetro de make_b).
fonte
Existe uma estrutura DI para C ++ (ainda em desenvolvimento AFAIK): Boost.DI .
Existem alguns comentários úteis sobre o framework no reddit.
fonte
Toda vez que uma pergunta como essa aparece, indica uma dependência para injetar . A idéia toda é "delegar" a responsabilidade fora do objeto que parece estar tendo problemas para descobrir como lidar com isso.
Usando seu exemplo, como a classe A parece não possuir "conhecimento" suficiente para descobrir como criar D, você inverte o controle e o expõe como uma dependência necessária para a classe A, exigindo que uma fábrica saiba como criar instâncias da classe D.
fonte