Estou refatorando uma classe e adicionando uma nova dependência a ela. A classe está atualmente assumindo suas dependências existentes no construtor. Portanto, para maior consistência, adiciono o parâmetro ao construtor.
Claro, existem algumas subclasses e ainda mais para testes de unidade, então agora estou jogando alterando todos os construtores para combinar, e isso está levando idades.
Isso me faz pensar que usar propriedades com setters é a melhor maneira de obter dependências. Eu não acho que dependências injetadas devam fazer parte da interface para construir uma instância de uma classe. Você adiciona uma dependência e agora todos os seus usuários (subclasses e qualquer pessoa que instancia você diretamente) repentinamente sabem disso. Parece uma quebra de encapsulamento.
Esse não parece ser o padrão com o código existente aqui, então estou procurando descobrir qual é o consenso geral, prós e contras de construtores versus propriedades. O uso de configuradores de propriedades é melhor?
fonte
Os usuários de uma classe devem saber sobre as dependências de uma determinada classe. Se eu tivesse uma classe que, por exemplo, estivesse conectada a um banco de dados e não fornecesse um meio de injetar uma dependência da camada de persistência, um usuário nunca saberia que uma conexão ao banco de dados teria que estar disponível. No entanto, se eu alterar o construtor, informarei aos usuários que há uma dependência da camada de persistência.
Além disso, para evitar que você precise alterar todo uso do construtor antigo, basta aplicar o encadeamento do construtor como uma ponte temporária entre o construtor antigo e o novo.
Um dos pontos da injeção de dependência é revelar quais dependências a classe possui. Se a classe tiver muitas dependências, talvez seja hora de uma refatoração: Todo método da classe usa todas as dependências? Caso contrário, é um bom ponto de partida para ver onde a classe pode ser dividida.
fonte
Obviamente, colocar o construtor significa que você pode validar tudo de uma vez. Se você atribuir itens a campos somente leitura, terá algumas garantias sobre as dependências do seu objeto desde o momento da construção.
É uma verdadeira dor adicionar novas dependências, mas pelo menos dessa maneira o compilador continua reclamando até que esteja correto. O que é uma coisa boa, eu acho.
fonte
Se você tiver um grande número de dependências opcionais (o que já é um cheiro), provavelmente a injeção de setter é o caminho a seguir. A injeção de construtores revela melhor suas dependências.
fonte
A abordagem geral preferida é usar a injeção do construtor o máximo possível.
A injeção de construtor indica exatamente quais são as dependências necessárias para que o objeto funcione corretamente - nada é mais irritante do que atualizar um objeto e fazer com que ele falhe ao chamar um método, porque alguma dependência não está definida. O objeto retornado por um construtor deve estar em um estado de trabalho.
Tente ter apenas um construtor, ele mantém o design simples e evita ambiguidade (se não for para humanos, para o contêiner DI).
Você pode usar a injeção de propriedade quando tiver o que Mark Seemann chama de padrão local em seu livro "Injeção de Dependências no .NET": a dependência é opcional porque você pode fornecer uma boa implementação de trabalho, mas deseja permitir que o chamador especifique outra se necessário.
(Resposta anterior abaixo)
Eu acho que a injeção de construtor é melhor se a injeção for obrigatória. Se isso adicionar muitos construtores, considere usar fábricas em vez de construtores.
A injeção do setter é boa se a injeção for opcional ou se você quiser alterá-la até a metade. Geralmente não gosto de levantadores, mas é uma questão de gosto.
fonte
É em grande parte uma questão de gosto pessoal. Pessoalmente, eu prefiro a injeção do setter, porque acredito que ela oferece mais flexibilidade na maneira de substituir implementações em tempo de execução. Além disso, construtores com muitos argumentos não são limpos na minha opinião, e os argumentos fornecidos em um construtor devem ser limitados a argumentos não opcionais.
Desde que a interface de classes (API) seja clara no que precisa para executar sua tarefa, você estará bem.
fonte
Pessoalmente, prefiro o "padrão" Extrair e Substituir a injetar dependências no construtor, principalmente pelo motivo descrito em sua pergunta. Você pode definir as propriedades como
virtual
e, em seguida, substituir a implementação em uma classe testável derivada.fonte
Prefiro injeção de construtor porque ajuda a "impor" os requisitos de dependência de uma classe. Se é na c'tor, um consumidor tem para definir os objetos para obter o aplicativo para compilar. Se você usar a injeção de incubadora, eles talvez não saibam que têm um problema até o tempo de execução - e, dependendo do objeto, pode ser tarde no tempo de execução.
Eu ainda uso a injeção de setter de tempos em tempos, quando o objeto injetado talvez precise de um monte de trabalho, como inicialização.
fonte
Eu prefiro a injeção de construtores, porque isso parece mais lógico. É como dizer que minha turma exige que essas dependências façam seu trabalho. Se é uma dependência opcional, as propriedades parecem razoáveis.
Também uso a injeção de propriedades para definir coisas às quais o contêiner não possui referências, como uma exibição do ASP.NET em um apresentador criado usando o contêiner.
Eu não acho que isso quebra o encapsulamento. O funcionamento interno deve permanecer interno e as dependências lidam com uma preocupação diferente.
fonte
Uma opção que pode valer a pena considerar é a composição de múltiplas dependências complexas a partir de dependências únicas simples. Ou seja, defina classes extras para dependências compostas. Isso torna as coisas um pouco mais fáceis de injeção do construtor WRT - menos parâmetros por chamada - enquanto mantém a coisa de fornecer todas as dependências para instanciar.
É claro que faz mais sentido se houver algum tipo de agrupamento lógico de dependências, portanto o composto é mais do que um agregado arbitrário e faz mais sentido se houver vários dependentes para uma única dependência composta - mas o bloco de parâmetros "padrão" tem existe há muito tempo, e a maioria dos que eu vi foram bastante arbitrários.
Pessoalmente, porém, sou mais fã do uso de métodos / configuradores de propriedades para especificar dependências, opções etc. Os nomes das chamadas ajudam a descrever o que está acontecendo. É uma boa idéia fornecer exemplos de trechos de como é configurá-lo e garantir que a classe dependente faça verificações de erro suficientes. Você pode querer usar um modelo de estado finito para a configuração.
fonte
Recentemente, tive uma situação em que tinha várias dependências em uma classe, mas apenas uma delas dependia necessariamente em cada implementação. Como as dependências de acesso a dados e registro de erros provavelmente só seriam alteradas para fins de teste, adicionei parâmetros opcionais para essas dependências e forneci implementações padrão dessas dependências no meu código de construtor. Dessa maneira, a classe mantém seu comportamento padrão, a menos que seja substituída pelo consumidor da classe.
O uso de parâmetros opcionais só pode ser realizado em estruturas que os suportam, como o .NET 4 (para C # e VB.NET, embora o VB.NET sempre os tenha). Obviamente, você pode obter funcionalidade semelhante simplesmente usando uma propriedade que pode ser reatribuída pelo consumidor da sua classe, mas você não obtém a vantagem da imutabilidade fornecida por ter um objeto de interface privada atribuído a um parâmetro do construtor.
Tudo isso dito, se você estiver introduzindo uma nova dependência que deve ser fornecida por todos os consumidores, precisará refatorar o construtor e todo o código que consome a sua classe. Minhas sugestões acima realmente se aplicam apenas se você tiver o luxo de poder fornecer uma implementação padrão para todo o seu código atual, mas ainda fornecer a capacidade de substituir a implementação padrão, se necessário.
fonte
Este é um post antigo, mas se for necessário no futuro, talvez seja útil:
https://github.com/omegamit6zeichen/prinject
Eu tive uma idéia semelhante e surgiu com essa estrutura. Provavelmente está longe de estar completo, mas é uma ideia de uma estrutura focada na injeção de propriedades
fonte
Depende de como você deseja implementar. Prefiro injeção de construtor sempre que sentir que os valores que entram na implementação não mudam com frequência. Por exemplo: se a estratégia de compnay for com o servidor oracle, configurarei meus valores de fonte de dados para um bean obtendo conexões via injeção de construtor. Caso contrário, se meu aplicativo for um produto e houver chances de ele se conectar a qualquer banco de dados do cliente, eu implementaria essa configuração de banco de dados e a implementação de várias marcas através da injeção de setter. Acabei de dar um exemplo, mas existem maneiras melhores de implementar os cenários mencionados acima.
fonte
A injeção de construtor revela explicitamente as dependências, tornando o código mais legível e menos propenso a erros de tempo de execução sem tratamento, se os argumentos são verificados no construtor, mas na verdade se resume à opinião pessoal e, quanto mais você usar o DI, mais você usará o DI. tendem a balançar para frente e para trás de uma maneira ou de outra, dependendo do projeto. Pessoalmente, tenho problemas com o cheiro do código, como construtores com uma longa lista de argumentos, e sinto que o consumidor de um objeto deve conhecer as dependências para poder usá-lo de qualquer maneira, portanto, é possível usar injeção de propriedade. Não gosto da natureza implícita da injeção de propriedades, mas acho mais elegante, resultando em um código com aparência mais limpa. Por outro lado, a injeção de construtores oferece um maior grau de encapsulamento,
Escolha a injeção por construtor ou por propriedade com base no seu cenário específico. E não pense que você precisa usar o DI apenas porque parece necessário e isso evitará maus cheiros de design e código. Às vezes, não vale a pena o esforço de usar um padrão se o esforço e a complexidade superam os benefícios. Mantenha simples.
fonte