2010-03-05 15 views
5

Betrachten Sie die folgende KlasseShould Klassenmethoden akzeptieren Parameter oder Verwendung Klasseneigenschaften

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

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

Um GetComplexResult ein Verbraucher dieser Klasse zu verwenden, würde wissen müssen A und B zu setzen, bevor die Methode aufgerufen wird. Wenn GetComplexResult auf viele Eigenschaften zugreift, um sein Ergebnis zu berechnen, kann dies zu falschen Rückgabewerten führen, wenn der Konsument nicht alle entsprechenden Eigenschaften zuerst festlegt. So können Sie diese Klasse schreiben wie diese statt

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

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

diese Weise kann ein Anrufer GetComplexResult ist gezwungen, in allen erforderlichen Werte zu übergeben, der erwartete Rückgabewert sichergestellt wird, korrekt berechnet. Wenn es jedoch viele erforderliche Werte gibt, wächst auch die Parameterliste, und das scheint auch kein gutes Design zu sein. Es scheint auch den Punkt der Kapselung von A, B und GetComplexResult in einer einzigen Klasse zu brechen. Ich könnte sogar versucht sein, GetComplexResult statisch zu machen, da es keine Instanz der Klasse benötigt, um seine Arbeit zu erledigen. Ich möchte nicht eine Reihe von statischen Methoden machen.

Gibt es Begriffe, um diese 2 verschiedenen Möglichkeiten zum Erstellen von Klassen zu beschreiben? Beide scheinen Vor- und Nachteile zu haben - gibt es etwas, das ich nicht verstehe, das mir sagen sollte, dass der eine Weg besser ist als der andere? Wie beeinflusst Unit Testing diese Wahl?

Antwort

5

Wenn Sie ein reales Beispiel verwenden, wird die Antwort klarer.

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

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

Der Punkt eines Entitätsobjekt ist, dass es Informationen über eine Entität enthält, und kann die Operationen tun, dass das Unternehmen (auf der Basis der Informationen, die sie enthält) tun muss. Es gibt also Situationen, in denen bestimmte Operationen nicht ordnungsgemäß funktionieren, weil die Entität nicht vollständig initialisiert wurde, aber das ist kein Konstruktionsfehler. Wenn ich dich in der realen Welt nach dem vollen Namen eines neugeborenen Babys frage, das noch nicht genannt wurde, wird das auch scheitern.

Wenn bestimmte Eigenschaften für eine Entität wichtig sind, die ihre Aufgabe erfüllt, können sie in einem Konstruktor initialisiert werden. Ein weiterer Ansatz ist es, einen boolean zu haben, der überprüft, ob das Unternehmen in einem Zustand befindet, in dem eine bestimmte Methode aufgerufen werden kann:

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

Ich versuche immer, Getter aufzurufen, anstatt direkt auf Mitglieder zuzugreifen, und im Getter protokolliere ich eine Nachricht, wenn ich einen Nullwert zurückgebe. Früher habe ich Assertions verwendet, aber dann haben wir festgestellt, dass einige unserer Kunden mit in der Produktion aktivierten Behauptungen arbeiten (!). – TMN

1

Eine gute Design-Regel, um sicherzustellen, dass alle Konstrukteure Objekte gültige Zustände initialisiert und dass alle Property Setter und Methoden erzwingen dann den gültigen Status. Auf diese Weise wird es niemals Objekte in ungültigen Zuständen geben.

Wenn die Standardwerte für A und B, die 0 ist kein gültiger Zustand, der ein gültiges Ergebnis aus GetComplexResult, sollten Sie einen Konstruktor liefert die A und B gültigen Zustand initialisiert.

0

Wenn einige der Felder niemals null sein dürfen, würden Sie sie normalerweise als Parameter für den Klassenkonstruktor definieren. Wenn nicht immer alle erforderlichen Werte gleichzeitig verfügbar sind, kann die Verwendung einer Builder-Klasse hilfreich sein.

Zum Beispiel:

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; } 
} 

Dieser Builder kann dann wie folgt verwendet werden.

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(); 

Dieses Design ermöglicht es Ihnen, die Entity-Klassen zu sperren, was auch immer Grad geeignet ist, während nach wie vor für einen flexiblen Bauprozess ermöglicht. Es öffnet auch die Tür zur Anpassung des Konstruktionsprozesses mit anderen Implementierungen, ohne den Entitätsklassenvertrag zu ändern.

Verwandte Themen