Estou apenas aprendendo sobre injeção de dependência e estou preso a alguma coisa. Injeção de Dependência recomenda o envio de classes dependentes através do construtor, mas estou me perguntando se isso é necessário para objetos de dados. Como a Unidade de Testabilidade é um dos principais benefícios da DI, um objeto de dados, que apenas armazena dados e nunca procedimentos, é testado em unidade, tornando a DI uma camada desnecessária de complexidade, ou ainda ajuda a mostrar dependências mesmo com objetos de dados?
Class DO{
DO(){
DataObject2List = new List<DO2>();
}
public string Field1;
public string Field2;
public List<DO2> DataObject2List;
}
Class DO2{
public DateTime Date;
public double Value;
}
c#
dependency-injection
class-design
sooprise
fonte
fonte
Respostas:
Gostaria de sugerir um esclarecimento sobre algumas das terminologias que você está usando aqui, especificamente "dependência" e "injeção de dependência".
Dependência:
Uma "dependência" é normalmente um objeto complexo que executa algumas funcionalidades das quais outra classe pode precisar depender. Alguns exemplos clássicos seriam um criador de logs ou um acessador de banco de dados ou algum componente que processe uma parte específica da lógica de negócios.
Um objeto apenas de dados, como um DTO ou um objeto de valor, normalmente não é chamado de "dependência", uma vez que eles não executam alguma função necessária.
Depois de olhar dessa maneira, o que você está fazendo no seu exemplo ( compondo o
DO
objeto com uma lista deD02
objetos por meio do construtor) não deve ser considerado "injeção de dependência". É apenas definir uma propriedade. Depende de você fornecê-lo no construtor ou de alguma outra maneira, mas simplesmente transmiti-lo pelo construtor não torna a injeção de dependência.Injeção de dependência:
Se sua
DO2
classe estivesse realmente fornecendo alguma funcionalidade adicional de que aDO
classe precisa, seria realmente uma dependência. Nesse caso, a classe dependenteDO
,, deve depender de uma interface (como ILogger ou IDataAccessor) e, por sua vez, contar com o código de chamada para fornecer essa interface (em outras palavras, para "injetar" naDO
instância).Injetar a dependência dessa maneira torna o
DO
objeto mais flexível, pois cada contexto diferente pode fornecer sua própria implementação da interface para oDO
objeto. (Pense em teste de unidade.)fonte
Eu farei o meu melhor para resolver a confusão na pergunta.
Primeiro de tudo, "Objeto de Dados" não é um termo significativo. Se a única característica definidora desse objeto é que ele não possui métodos, ele não deveria existir . Um objeto sem comportamento útil deve caber em pelo menos uma das seguintes subcategorias:
Objetos de valor ou "registros" não têm identidade. Eles devem ser tipos de valor , com semântica de cópia na referência, assumindo que o ambiente o suporte. Como essas são estruturas fixas, um VO deve sempre ser um tipo primitivo ou uma sequência fixa de primitivos. Portanto, um VO não deve ter nenhuma dependência ou associação; qualquer construtor não padrão existiria apenas com o objetivo de inicializar o valor, ou seja, porque ele não pode ser expresso como literal.
Os objetos de transferência de dados geralmente são confundidos por engano com objetos de valor. DTOs fazer têm identidades, ou pelo menos eles podem . O único objetivo de um DTO é facilitar o fluxo de informações de um domínio para outro. Eles nunca têm "dependências". Eles podem ter associações (isto é, a uma matriz ou coleção), mas a maioria das pessoas prefere deixá-las planas. Basicamente, eles são análogos às linhas na saída de uma consulta ao banco de dados; são objetos transitórios que geralmente precisam ser persistidos ou serializados e, portanto, não podem fazer referência a nenhum tipo abstrato, pois isso os tornaria inutilizáveis.
Por fim, o Data Access Objects fornece um invólucro ou fachada a um banco de dados de algum tipo. Obviamente, eles têm dependências - eles dependem da conexão com o banco de dados e / ou dos componentes de persistência. No entanto, suas dependências são quase sempre gerenciadas externamente e totalmente invisíveis para os chamadores. No padrão Active Record , é a estrutura que gerencia tudo através da configuração; nos modelos DAO mais antigos (antigos para os padrões atuais), você só podia construí-los através do contêiner. Se eu visse um desses com injeção de construtor, ficaria muito, muito preocupado.
Você também pode estar pensando em um objeto de entidade ou "objeto de negócios" e, nesse caso , deseja oferecer suporte à injeção de dependência, mas não da maneira que pensa ou pelos motivos que pensa. Não é para o benefício do código do usuário , é para o benefício de um gerente de entidade ou ORM, que injeta silenciosamente um proxy que ele intercepta para fazer coisas sofisticadas, como compreensão de consultas ou carregamento lento.
Nestas, você geralmente não fornece um construtor para injeção; em vez disso, você só precisa tornar a propriedade virtual e usar um tipo abstrato (por exemplo, em
IList<T>
vez deList<T>
). O resto acontece nos bastidores, e ninguém é o mais sábio.Então, apesar de tudo, eu diria que um padrão de DI visível sendo aplicado a um "objeto de dados" é desnecessário e provavelmente até uma bandeira vermelha; mas, em grande parte, isso ocorre porque a própria existência do objeto é uma bandeira vermelha, exceto no caso em que está sendo especificamente utilizado para representar dados de um banco de dados. Em quase todos os outros casos, é um cheiro de código, geralmente o começo de um Modelo de Domínio Anêmico ou, no mínimo, de um Poltergeist .
Reiterar:
Fin.
fonte
No seu exemplo,
DO
não possui dependências funcionais (basicamente porque não faz nada). Ele depende do tipo concretoDO2
, portanto, você pode querer introduzir uma interface para abstrairDO2
, para que o consumidor possa implementar sua própria implementação concreta da classe filho.Realmente, que dependência você injetaria aqui?
fonte
DOPersister
que sabe persistirDO
e deixá-lo como um objeto estritamente somente de dados (melhor na minha opinião). No último caso,DOPersister
seria injetado com a dependência do banco de dados.Como esse é um objeto de dados na camada de acesso a dados, ele deve depender diretamente de um serviço de banco de dados. Você pode especificar um DatabaseService para o construtor:
Mas, a injeção não precisa estar no construtor. Como alternativa, você pode fornecer a dependência por meio de cada método CRUD. Prefiro esse método ao anterior, porque seu Data Object não precisa saber onde ele persistirá até que você realmente precise persistir.
Você definitivamente não deseja ocultar a construção nos métodos CRUD!
Uma opção alternativa seria construir o DatabaseService por meio de um método de classe substituível.
Uma alternativa final é usar um ServiceLocator no estilo singleton. Embora eu não goste desta opção, ela é testável por unidade.
fonte