La question pourrait sembler un peu bizarre et je suppose que c'est. Je suis proposé la question en naviguant à travers des modèles de conception. Je suis venu au schéma notoire de l'état/de la stratégie et je suis venu avec ce code:
public class Account
{
public double Balance { get; private set; }
private IAccountState state;
public Account()
{
state = new ActiveAccountState();
}
public void Deposit(double number)
{
state = state.Deposit(() => Balance += number);
}
public void Withdraw(double number)
{
state = state.Withdraw(() => Balance -= number);
}
public void Freeze()
{
state = state.Freeze();
}
public void Close()
{
state = state.Close();
}
}
C'est simpliste et pas la production prête bien sûr, mais je n'essaie pas que ce soit cela. C'est un exemple simple. Ce code montre bien bien le problème que j'ai. Fondamentalement, j'ai une interface IACCountate qui représente l'état/la stratégie. Maintenant, cela pourrait être considéré comme une dépendance et en permettant à la classe de compte de savoir comment fabriquer l'activité ActiveAccountState, je permet à la classe de compte de disposer de plus de 1 responsabilités. Je pourrais créer un compte de compte et faire une dépendance à la classe de compte. Cela me permettrait d'injecter facilement cette nouvelle dépendance en usine nouvelle avec l'utilisation du constructeur de classe de compte. Mais de cette façon, j'expose les détails de la mise en œuvre qui ne sont probablement intéressants que pour la classe de compte et à néanmoins. Je suppose que cela faciliterait le test de l'unité un peu de tad.
Ma question serait de savoir comment savoir si quelque chose devrait être une dépendance? Parfois, il est simple simple (c'est-à-dire des services, des usines, des fournisseurs, etc.), mais il y a ensuite des moments où je suis confus (comme dans cet exemple de cet état/de la stratégie). Merci d'avance!
comment savoir si quelque chose devrait être une dépendance?
Chaque fois que vous avez besoin de quelque chose, mais que vous ne voulez pas le faire ici.
La grande chose que j'aimerais que vous compreniez sur la transmission de la référence (ou une injection de dépendance si vous insistez pour être fantaisie) est que l'utilisation d'une bonne valeur par défaut est correcte. Vous n'êtes pas interdit d'utiliser de nouveau. Il vous est interdit d'utiliser de nouveaux sans fournir un moyen de remplacer ce qui vous fait remarquer.
Ceci est mal compris dans les langues (comme Java) qui n'ont pas nommé de paramètres. C # les a pour que vous ayez des gars sans excuse. Josh Bloch a donné Java utilisateurs Son modèle de constructeur Pour résoudre ce problème, donc si vous le savez, vous n'avez pas non plus d'excuse.
Les paramètres nommés facilitent d'avoir des arguments facultatifs. Si vous avez que vous pouvez avoir des valeurs par défaut facilement. Votre état devrait être une valeur par défaut ultra-fiable. Cela vous permet d'utiliser DI sans souffrir avec des classes impuissantes qui ne peuvent pas se construire elles-mêmes.
La philosophie de cette approche différente de DI est appelée Convention sur la configuration . Essayez-le. Je parie que vous trouverez que cela rend ces idées beaucoup moins théoriques et plus utiles.
Il y a d'autres problèmes que vous n'avez pas posé de questions sur et puisque ce n'est pas revue de code Je ne vais pas entrer dans tous, mais je ne peux tout simplement pas ignorer le double Balance
une. S'il vous plaît dites-moi que cela ne représente pas des dollars et des cents. Double fonctionne bien sur pouces discret (base 2) et NONDISCRETE/mesures continues (sans base) mais pas sur la base 10 devises. IEEE Points flottants et comptables s'entendre rarement .
Vous n'avez pas fourni de séparation de l'état/stratégie ou de réduire les responsabilités de compte. À un appelant de compte Il n'y a pas de séparation entre compte et ActiveAccountState. Tout appel à des méthodes de compte appellera également des méthodes de ActiveAccountState. Le résultat attendu dépendra donc de la catégorie Compte et ActiveAccountState classe. Tests unitaires pour compte sera sensible aux modifications apportées à ActiveAccountState.
Si vous les séparez vraiment en injectant une instance IACCountState instance dans compte, vous pouvez alors tester compte Utilisation d'une implémentation moquée de IACCounteState. Ces tests ne seront plus sensibles aux modifications apportées à ActiveAccountState. Cela compliquera la création de compte Les instances et une classe d'usine ou un conteneur d'injection de dépendance peut être utile.
Comment savoir? Vous pouvez essayer d'écrire votre test de code - d'abord. Ce n'est pas amusant d'écrire des tests compliqués. Vous commencerez donc à optimiser votre conception de classe pour faciliter les tests. Code qui est facile à tester est facile à entretenir.
BTW, pourquoi est balance un membre de compte? Sûrement Balance devrait appartenir à l'État du compte.
Je suis pour moi-même appliquer le modèle de responsabilité unique à ce problème aussi:
Une classe fournissant comportement commercial ne devrait pas être responsable de l'instanciation de ses dépendances.
Donc, en cas de doute favorable à DI.
Ma question serait de savoir comment savoir si quelque chose devrait être une dépendance?
À partir d'un solide OO La conception de classe, les dépendances ont tendance à devenir évidentes.
IAccountState
invente une dépendance en découplant de manière inappropriée le comportement et l'état d'une classe par ailleurs cohérente. Pourtant, vous frappez pratiquement le clou sur la tête: "I, Eh bien, exposez les détails de la mise en œuvre qui ne sont probablement intéressants que pour la classe de compte et à néant"
Une classe abstraite dans ce cas peut résoudre la fracturation de la classe tout en insistant sur un modèle de stratégie et éliminer ainsi le problème de dépendance artificielle. Et partout où il y a un constructeur ou une méthode - béton, abstrait ou sinon, il y a une couture pour l'injection de dépendance.
Donc, j'en suis désaccord avec enthousiasme avec l'éloignement de l'héritage parce que "vous avez une opportunité limitée pour DI ... comme vous faites cuire la logique ... à travers sa chaîne héritabilité". Vous devriez avoir conçu Opportunités de DI, pas une liberté pour tous violer les locataires de l'orientation de l'objet.
ne bonne classe expose les fonctionnalités et les cachets
La clé identifie cette responsabilité unique pour chaque classe. Pensez la fonctionnalité d'abord, alors l'état nécessaire pour le soutenir. Ceci est un chemin plus fiable vers une bonne application SRP.
Dépendances de conception diligente de conception
Ne laissez pas l'exubérance de interface
s Hobble SRP Application et déchirez prématurément l'état et/ou la fonctionnalité.
L'héritage et la composition sont complémentaires. Laissez cela évoluer pendant l'analyse et la conception. En fait, je m'attends à le voir juste comme n'importe quelle machine est faite de pièces distinctes.
N'approchez pas cela d'une solution à la recherche d'une position de problème.
Utilisez toujours di lorsque vous avez séparé vos préoccupations à l'aide de la composition.
Le fait même que vous utilisez la composition implique que vous souhaitiez échanger la dépendance, si seulement avec une simule pour les tests.
Si vous utilisez une héritage à la place, vous avez une opportunité limitée pour DI lorsque vous faites cuire la logique dans la classe de béton à travers sa chaîne de héritage.
Donc, dans votre exemple, vous avez peut-être dû dire des entreprises et des comptes courants avec une logique de retrait différente gérée par différents objets de compte injectés.
Si vous utilisiez l'héritage, vous créeriez des catégories d'affaires et courants d'entreprise distinctes qui remplacent la méthode de retrait