É uma boa pergunta - mas, em algum momento, o encapsulamento em sua forma mais pura precisa ser violado para que o objeto tenha sua dependência cumprida. Algum provedor da dependência deve saber que o objeto em questão requer um Foo
e que o provedor precisa ter uma maneira de fornecer oFoo
o objeto.
Classicamente, este último caso é tratado como você diz, por meio de argumentos construtores ou métodos setter. No entanto, isso não é necessariamente verdade - eu sei que as versões mais recentes do framework Spring DI em Java, por exemplo, permitem anotar campos particulares (por exemplo, com@Autowired
) e a dependência será definida por meio de reflexão sem a necessidade de expor a dependência por meio de qualquer uma das classes métodos / construtores públicos. Esse pode ser o tipo de solução que você estava procurando.
Dito isto, também não acho que a injeção de construtores seja um problema. Eu sempre achei que os objetos deveriam ser totalmente válidos após a construção, de forma que tudo o que eles precisassem para desempenhar sua função (ou seja, estar em um estado válido) deveria ser fornecido pelo construtor de qualquer maneira. Se você tem um objeto que exige que um colaborador funcione, parece-me bom que o construtor anuncie publicamente esse requisito e garanta que ele seja cumprido quando uma nova instância da classe for criada.
Idealmente, ao lidar com objetos, você interage com eles por meio de uma interface, e quanto mais você faz isso (e tem dependências conectadas por meio do DI), menos você realmente precisa lidar com os construtores. Na situação ideal, seu código não lida com nem cria instâncias concretas de classes; então ele recebe um IFoo
DI direto, sem se preocupar com o que o construtor deFooImpl
indica que precisa fazer seu trabalho e, de fato, sem nem mesmo estar ciente da FooImpl
existência do mesmo. Deste ponto de vista, o encapsulamento é perfeito.
Essa é uma opinião, é claro, mas, na minha opinião, o DI não viola necessariamente o encapsulamento e, de fato, pode ajudá-lo, centralizando todo o conhecimento necessário dos internos em um só lugar. Isso não é apenas uma coisa boa por si só, mas ainda melhor este lugar está fora da sua própria base de código, portanto, nenhum código que você escreve precisa saber sobre as dependências das classes.