J'apprends juste sur l'injection de dépendance et je suis coincé sur quelque chose. L'injection de dépendance recommande d'envoyer des classes dépendantes via le constructeur, mais je me demande si cela est nécessaire pour les objets de données. Étant donné que la testabilité unitaire est l'un des principaux avantages de DI, un objet de données, qui ne stocke que des données, et aucune procédure ne sera jamais testée unitaire, faisant de DI une couche de complexité inutile, ou aide-t-il toujours à montrer les dépendances même avec des objets de données?
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
la source
la source
Réponses:
Je voudrais suggérer une clarification de la terminologie que vous utilisez ici, en particulier "dépendance" et "injection de dépendance".
Dépendance:
Une "dépendance" est généralement un objet complexe qui exécute certaines fonctionnalités dont une autre classe peut avoir besoin. Quelques exemples classiques seraient un enregistreur ou un accesseur de base de données ou un composant qui traite un élément particulier de la logique métier.
Un objet de données uniquement comme un DTO ou un objet de valeur n'est généralement pas appelé une "dépendance", car ils n'exécutent pas certaines fonctions nécessaires.
Une fois que vous le regardez de cette façon, ce que vous faites dans votre exemple ( composer l'
DO
objet avec une liste d'D02
objets via le constructeur) ne doit pas du tout être considéré comme une "injection de dépendance". Il s'agit simplement de définir une propriété. C'est à vous de décider si vous le fournissez dans le constructeur ou d'une autre manière, mais le simple fait de le transmettre via le constructeur ne fait pas de l'injection de dépendances.Injection de dépendance:
Si votre
DO2
classe fournissait en fait des fonctionnalités supplémentaires dont laDO
classe a besoin, ce serait vraiment une dépendance. Dans ce cas, la classe dépendanteDO
,, devrait dépendre d'une interface (comme ILogger ou IDataAccessor), et à son tour s'appuyer sur le code appelant pour fournir cette interface (en d'autres termes, pour l'injecter dans l'DO
instance).L'injection de la dépendance de cette manière rend l'
DO
objet plus flexible, car chaque contexte différent peut fournir sa propre implémentation de l'interface à l'DO
objet. (Pensez aux tests unitaires.)la source
Je vais faire de mon mieux pour éliminer la confusion dans la question.
Tout d'abord, "Data Object" n'est pas un terme significatif. Si la seule caractéristique qui définit cet objet est qu'il n'a pas de méthodes, alors il ne devrait pas exister du tout . Un objet utile sans comportement doit tenir dans au moins une des sous-catégories suivantes:
Les objets de valeur ou «enregistrements» n'ont aucune identité. Il doit s'agir de types de valeurs , avec une sémantique de copie sur référence, en supposant que l'environnement le prend en charge. Puisqu'il s'agit de structures fixes, une VO ne doit être qu'un type primitif ou une séquence fixe de primitives. Par conséquent, une VO ne devrait pas avoir de dépendances ou d' associations; tout constructeur non par défaut existerait uniquement dans le but d'initialiser la valeur, c'est-à-dire parce qu'elle ne peut pas être exprimée comme un littéral.
Les objets de transfert de données sont souvent confondus par erreur avec des objets de valeur. DTO faire avoir des identités, ou du moins ils le peuvent . Le seul objectif d'un DTO est de faciliter la circulation des informations d'un domaine à l'autre. Ils n'ont jamais de "dépendances". Ils peuvent avoir des associations (c'est-à-dire à un tableau ou à une collection) mais la plupart des gens préfèrent les rendre plats. Fondamentalement, ils sont analogues aux lignes de la sortie d'une requête de base de données; ce sont des objets transitoires qui doivent généralement être persistés ou sérialisés, et ne peuvent donc pas référencer de types abstraits, car cela les rendrait inutilisables.
Enfin, les objets d'accès aux données fournissent un wrapper ou une façade à une base de données quelconque. Ceux-ci ont évidemment des dépendances - ils dépendent de la connexion à la base de données et / ou des composants de persistance. Cependant, leurs dépendances sont presque toujours gérées en externe et totalement invisibles pour les appelants. Dans le modèle Active Record , c'est le cadre qui gère tout à travers la configuration; dans les modèles DAO plus anciens (anciens selon les normes actuelles), vous ne pouviez les construire que via le conteneur. Si j'en voyais un avec l'injection de constructeur, je serais très, très inquiet.
Vous pouvez également penser à un objet entité ou « objet métier » , et dans ce cas , vous ne voulez soutenir l' injection de dépendance, mais pas de la manière que vous pensez ou pour les raisons que vous pensez. Ce n'est pas au profit du code utilisateur , c'est au profit d'un gestionnaire d'entités ou ORM, qui injectera silencieusement un proxy qu'il intercepte pour faire des choses fantaisistes comme la compréhension des requêtes ou le chargement paresseux.
Dans ceux-ci, vous ne fournissez généralement pas de constructeur pour l'injection; au lieu de cela, il vous suffit de rendre la propriété virtuelle et d'utiliser un type abstrait (par exemple
IList<T>
au lieu deList<T>
). Le reste se passe dans les coulisses, et personne n'est plus sage.Donc, dans l'ensemble, je dirais qu'un modèle DI visible appliqué à un "objet de données" est inutile et probablement même un drapeau rouge; mais en grande partie, c'est parce que l' existence même de l'objet est un drapeau rouge, sauf dans le cas où il est spécifiquement utilisé pour représenter des données d'une base de données. Dans presque tous les autres cas, c'est une odeur de code, généralement les débuts d'un modèle de domaine anémique ou à tout le moins un Poltergeist .
Recommencer:
Ailette.
la source
Dans votre exemple,
DO
n'a pas de dépendances fonctionnelles (essentiellement parce qu'il ne fait rien). Il dépend du type concretDO2
, vous voudrez peut-être introduire une interface pour l'abstraitDO2
, afin que le consommateur puisse implémenter sa propre implémentation concrète de la classe enfant.Vraiment, quelle dépendance injecteriez-vous ici?
la source
DOPersister
qui sait persisterDO
et le laisser comme un objet strictement data-only (mieux à mon avis). Dans ce dernier cas,DOPersister
serait injecté avec la dépendance de la base de données.Comme il s'agit d'un objet de données dans la couche d'accès aux données, il doit dépendre directement d'un service de base de données. Vous pouvez spécifier un DatabaseService au constructeur:
Mais, l'injection n'a pas besoin d'être dans le constructeur. Alternativement, vous pouvez fournir la dépendance via chaque méthode CRUD. Je préfère cette méthode à la précédente car votre objet de données n'a pas besoin de savoir où il persistera jusqu'à ce que vous ayez réellement besoin de le conserver.
Vous ne voulez certainement pas cacher la construction dans les méthodes CRUD!
Une autre option serait de construire le DatabaseService via une méthode de classe surchargeable.
Une dernière alternative consiste à utiliser un ServiceLocator de style singleton. Bien que je n'aime pas cette option, elle est testable à l'unité.
la source