2010-03-05 8 views
5

Tenir compte de la classe suivanteméthodes de classe devraient accepter les paramètres ou l'utilisation des propriétés de classe

public class Class1 
{ 
    public int A { get; set; } 
    public int B { get; set; } 

    public int GetComplexResult() 
    { 
     return A + B; 
    } 
} 

Pour utiliser GetComplexResult, un consommateur de cette classe devrait savoir pour mettre A et B avant d'appeler la méthode. Si GetComplexResult accède à de nombreuses propriétés pour calculer son résultat, cela peut entraîner des valeurs de retour erronées si le consommateur ne définit pas toutes les propriétés appropriées en premier. Donc, vous pourriez écrire cette classe comme celui-ci au lieu

public class Class2 
{ 
    public int A { get; set; } 
    public int B { get; set; } 

    public int GetComplexResult(int a, int b) 
    { 
     return a + b; 
    } 
} 

De cette façon, un appelant à GetComplexResult est forcée de passer toutes les valeurs requises, veiller à la valeur de rendement attendu est calculé correctement. Mais s'il y a beaucoup de valeurs requises, la liste de paramètres augmente également et cela ne semble pas non plus être un bon design. Il semble également rompre le point d'encapsulation A, B et GetComplexResult dans une seule classe. Je pourrais même être tenté de rendre statique GetComplexResult car il ne nécessite pas une instance de la classe pour faire son travail. Je ne veux pas faire un tas de méthodes statiques.

Existe-t-il des termes pour décrire ces 2 différentes façons de créer des classes? Ils semblent tous deux avoir des avantages et des inconvénients - y a-t-il quelque chose que je ne comprends pas qui devrait me dire qu'une façon est meilleure que l'autre? Comment les tests unitaires influencent-ils ce choix?

Répondre

5

Si vous utilisez un exemple concret, la réponse devient plus claire.

public class person 
{ 
    public string firstName { get; set; } 
    public string lastName { get; set; } 

    public string getFullName() 
    { 
     return firstName + " " + lastName; 
    } 
} 

Le point d'un objet d'entité est qu'il contient des informations sur une entité, et peut effectuer les opérations que l'entité doit faire (sur la base des informations qu'il contient). Donc oui, il y a des situations dans lesquelles certaines opérations ne fonctionneront pas correctement parce que l'entité n'a pas été complètement initialisée, mais ce n'est pas un échec de conception. Si, dans le monde réel, je vous demande le nom complet d'un nouveau-né qui n'a pas encore été nommé, cela échouera également.

Si certaines propriétés sont essentielles à une entité effectuant son travail, elles peuvent être initialisées dans un constructeur. Une autre approche est d'avoir un booléen qui vérifie si l'entité est dans un état où une méthode donnée peut être appelé:

while (person.hasAnotherQuestion()) { 
    person.answerNextQuestion(); 
} 
+0

J'essaie toujours d'appeler des getters au lieu d'accéder directement aux membres, et dans le getter je consigne un message si je retourne une valeur nulle. J'avais l'habitude d'utiliser des assertions, mais ensuite nous avons découvert que certains de nos clients s'exécutent avec des assertions activées en production (!). – TMN

1

Une bonne règle de conception est de faire en sorte que tous les constructeurs initialisent objets à états valides et que tous les les régleurs de propriété et les méthodes appliquent ensuite l'état valide. De cette façon, il n'y aura jamais d'objets dans des états invalides.

Si les valeurs par défaut pour A et B, ce qui est 0 est pas un état valide qui donne un résultat valable à partir GetComplexResult, vous devriez un constructeur qui a initialisé A et B pour valider un état.

0

Si certains des champs ne sont jamais autorisés à être NULL, vous devez généralement les rendre au constructeur de la classe. Si vous ne disposez pas toujours de toutes les valeurs requises en même temps, il peut être utile d'utiliser une classe de générateur.

Par exemple:

public Builder { 
    private int a; 
    private int b; 

    public Class1 create() { 
     // some validation logic goes here 
     // to make sure we have everything and 
     // either fill in defaults or throw an error 
     // if needed 
     return new Class1(a, b) 
    } 

    public Builder a(int val) { a = val; } 
    public Builder b(int val) { b = val; } 
} 

Ce constructeur peut ensuite être utilisé comme suit.

Class1 obj1 = new Builder().a(5).b(6).create(); 

Builder builder = new Builder(); 
// do stuff to find value of a 
builder.a(valueOfA); 
// do stuff to find value of b 
builder.b(valueOfB); 
// do more stuff 
Class1 obj2 = builder.create(); 
Class2 obj3 = builder.create(); 

Cette conception vous permet de verrouiller les classes Entity à n'importe quel degré approprié tout en permettant un processus de construction flexible. Il ouvre également la porte à la personnalisation du processus de construction avec d'autres implémentations sans changer le contrat de classe d'entité.

Questions connexes