Nous faisons beaucoup de tests unitaires et de refactorisation de nos objets métier, et je semble avoir des opinions très différentes sur la conception des classes que les autres pairs.
Un exemple de cours dont je ne suis pas fan:
public class Foo
{
private string field1;
private string field2;
private string field3;
private string field4;
private string field5;
public Foo() { }
public Foo(string in1, string in2)
{
field1 = in1;
field2 = in2;
}
public Foo(string in1, string in2, string in3, string in4)
{
field1 = in1;
field2 = in2;
field3 = in3;
}
public Prop1
{ get { return field1; } }
{ set { field1 = value; } }
public Prop2
{ get { return field2; } }
{ set { field2 = value; } }
public Prop3
{ get { return field3; } }
{ set { field3 = value; } }
public Prop4
{ get { return field4; } }
{ set { field4 = value; } }
public Prop5
{ get { return field5; } }
{ set { field5 = value; } }
}
Dans la classe "réelle", elles ne sont pas toutes des chaînes, mais dans certains cas, nous avons 30 champs de support pour les propriétés complètement publiques.
Je déteste ce cours et je ne sais pas si je suis juste difficile. Quelques points importants:
- Champs de sauvegarde privés sans logique dans les propriétés, semble inutile et gonfle la classe
- Constructeurs multiples (assez bien) mais couplés à
- toutes les propriétés ayant un setter public, je ne suis pas fan.
- Aucune propriété ne serait potentiellement affectée à une valeur en raison du constructeur vide, si un appelant n'est pas au courant, vous pourriez potentiellement obtenir des comportements très indésirables et difficiles à tester.
- C'est trop de propriétés! (dans le cas 30)
Je trouve beaucoup plus difficile de vraiment savoir dans quel état se trouve l'objet Foo
à un moment donné, en tant qu'implémenteur. L'argument a été avancé: «nous pourrions ne pas avoir les informations nécessaires à définir Prop5
au moment de la construction de l'objet. D'accord, je suppose que je peux le comprendre, mais si c'est le cas, ne rendre Prop5
public que le setter, pas toujours jusqu'à 30 propriétés dans une classe.
Suis-je juste pointilleux et / ou fou pour vouloir une classe qui est "facile à utiliser" par opposition à "facile à écrire (tout public)"? Des cours comme ceux ci-dessus me crient, je ne sais pas comment ça va être utilisé, donc je vais juste rendre tout public juste au cas où .
Si je ne suis pas très difficile, quels sont les bons arguments pour lutter contre ce type de pensée? Je ne suis pas très bon pour articuler des arguments, car je suis très frustré d'essayer de faire passer mon message (pas intentionnellement bien sûr).
get
ouset
:-)Réponses:
Les classes entièrement publiques ont une justification pour certaines situations, ainsi que pour les autres classes extrêmes, avec une seule méthode publique (et probablement beaucoup de méthodes privées). Et des cours avec des méthodes publiques et privées aussi.
Tout dépend du type d'abstraction que vous allez modéliser avec eux, des couches que vous avez dans votre système, du degré d'encapsulation dont vous avez besoin dans les différentes couches et (bien sûr) de quelle école de pensée l'auteur de la classe vient de. Vous pouvez trouver tous ces types dans le code SOLID.
Il y a des livres entiers écrits sur le moment de préférer quel type de conception, donc je ne vais pas énumérer ici de règles à ce sujet, l'espace dans cette section ne serait pas suffisant. Cependant, si vous avez un exemple réel d'une abstraction que vous aimez modéliser avec une classe, je suis sûr que la communauté ici vous aidera avec plaisir à améliorer la conception.
Pour aborder vos autres points:
Donc au lieu de
écrire
ou
"Constructeurs multiples": ce n'est pas un problème en soi. Il y a un problème lorsqu'il y a duplication de code inutile, comme indiqué dans votre exemple, ou lorsque la hiérarchie des appels est compliquée. Cela peut être facilement résolu en refactorisant les parties communes dans une fonction distincte et en organisant la chaîne du constructeur de manière unidirectionnelle
"Aucune propriété ne serait potentiellement affectée à cause du constructeur vide": en C #, chaque type de données a une valeur par défaut clairement définie. Si les propriétés ne sont pas initialisées explicitement dans un constructeur, elles obtiennent cette valeur par défaut affectée. Si cela est utilisé intentionnellement , c'est tout à fait correct - donc un constructeur vide pourrait être correct si l'auteur sait ce qu'il fait.
"C'est trop de propriétés! (Dans le cas 30)": oui, si vous êtes libre de concevoir une telle classe de manière entièrement nouvelle, 30 sont trop, je suis d'accord. Cependant, tout le monde n'a pas ce luxe (n'avez-vous pas écrit dans le commentaire ci-dessous qu'il s'agit d'un système hérité?). Parfois, vous devez mapper des enregistrements d'une base de données ou d'un fichier existant, ou des données d'une API tierce à votre système. Donc, pour ces cas, 30 attributs peuvent être quelque chose avec lequel on doit vivre.
la source
null
-ce pas? donc si l'une des propriétés qui est réellement utilisée dans a.ToUpper()
, mais qu'une valeur n'est jamais définie pour elle, elle lèverait une exception d'exécution. Ceci est un autre bon exemple pourquoi les requis des données pour la classe devraient avoir à régler lors de la construction de l' objet. Pas seulement laisser le soin à l'utilisateur. THanksEn disant que "les champs de sauvegarde privés sans logique dans les propriétés, semblent inutiles et gonflent la classe", vous avez déjà un argument décent.
Il ne semble pas que plusieurs constructeurs soient en cause ici.
Quant à avoir toutes les propriétés publiques ... Peut-être pensez-vous de cette façon, si vous avez toujours voulu synchroniser l'accès à toutes ces propriétés entre les threads, vous auriez du mal car vous pourriez avoir à écrire du code de synchronisation partout où ils sont utilisé. Cependant, si toutes les propriétés étaient enfermées par des getters / setters, vous pourriez facilement créer la synchronisation dans la classe.
Je pense que lorsque vous dites "comportement difficile à tester", l'argument parle de lui-même. Votre test devra peut-être vérifier s'il n'est pas nul ou quelque chose comme ça (chaque endroit où la propriété est utilisée). Je ne sais pas vraiment parce que je ne sais pas à quoi ressemble votre test / application.
Vous avez bien trop de propriétés, pourriez-vous essayer d'utiliser l'héritage (si les propriétés sont assez similaires) et ensuite avoir une liste / tableau utilisant des génériques? Ensuite, vous pouvez écrire des getters / setters pour accéder aux informations et simplifier la façon dont vous interagirez avec toutes les propriétés.
la source
Comme vous me l'avez dit,
Foo
c'est un objet commercial. Il doit encapsuler certains comportements dans les méthodes en fonction de votre domaine et ses données doivent rester aussi encapsulées que possible.Dans l'exemple que vous montrez,
Foo
ressemble plus à un DTO et va à l'encontre de tous les principes de la POO (voir Modèle de domaine anémique )!Comme améliorations, je suggère:
Cela pourrait être une classe refactorisée potentielle avec ces remarques:
la source
"Nous pouvons maintenant tester unitairement ces méthodes refactorisées. En l'état, pour des raisons x, y, z, le client n'est pas testable.
Dans la mesure où l'un des arguments ci-dessus peut être avancé, dans l'ensemble:
la source