6 Stimmen

Sollten Klassenmethoden Parameter akzeptieren oder Klasseneigenschaften verwenden?

Betrachten Sie die folgende Klasse

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

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

Für die Verwendung von GetComplexResult müsste ein Verbraucher dieser Klasse wissen, dass er die A y B bevor Sie die Methode aufrufen. Wenn GetComplexResult auf viele Eigenschaften zugreift, um sein Ergebnis zu berechnen, kann dies zu falschen Rückgabewerten führen, wenn der Verbraucher nicht zuerst alle geeigneten Eigenschaften einstellt. Sie könnten diese Klasse also stattdessen so schreiben

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

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

Auf diese Weise kann ein Aufrufer von GetComplexResult ist gezwungen, alle erforderlichen Werte einzugeben, damit der erwartete Rückgabewert korrekt berechnet wird. Wenn jedoch viele Werte erforderlich sind, wird die Parameterliste immer länger, was ebenfalls nicht gut aussieht. Es scheint auch den Sinn der Kapselung zu verletzen A , B y GetComplexResult in einer einzigen Klasse. Ich könnte sogar in Versuchung kommen, die GetComplexResult statisch, da sie keine Instanz der Klasse benötigt, um ihre Arbeit zu verrichten. Ich möchte nicht herumlaufen und einen Haufen statischer Methoden erstellen.

Gibt es Begriffe, die diese beiden unterschiedlichen Arten der Klassenbildung beschreiben? Beide scheinen Vor- und Nachteile zu haben - gibt es etwas, das ich nicht verstehe, das mir sagen sollte, dass eine Methode besser ist als die andere? Welchen Einfluss hat das Unit Testing auf diese Entscheidung?

6voto

Jacob Mattison Punkte 48729

Anhand eines Beispiels aus der Praxis wird die Antwort klarer.

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

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

Der Sinn eines Entitätsobjekts besteht darin, dass es Informationen über eine Entität enthält und die Operationen ausführen kann, die die Entität ausführen muss (basierend auf den enthaltenen Informationen). Ja, es gibt Situationen, in denen bestimmte Operationen nicht richtig funktionieren, weil die Entität nicht vollständig initialisiert wurde, aber das ist kein Fehler im Design. Wenn ich Sie in der realen Welt nach dem vollständigen Namen eines neugeborenen Babys frage, das noch keinen Namen hat, wird auch das fehlschlagen.

Wenn bestimmte Eigenschaften für eine Entität, die ihre Aufgabe erfüllt, wesentlich sind, können sie in einem Konstruktor initialisiert werden. Ein anderer Ansatz ist ein Boolescher Wert, der prüft, ob sich die Entität in einem Zustand befindet, in dem eine bestimmte Methode aufgerufen werden kann:

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

2voto

Anders Abel Punkte 65873

Eine gute Entwurfsregel ist es, sicherzustellen, dass alle Konstruktoren Objekte in gültige Zustände initialisieren und dass alle Eigenschaftssetzer und Methoden dann den gültigen Zustand erzwingen. Auf diese Weise wird es nie Objekte in ungültigen Zuständen geben.

Wenn die Standardwerte für A y B mit dem Wert 0 ist kein gültiger Zustand, der ein gültiges Ergebnis von GetComplexResult sollten Sie einen Konstruktor verwenden, der die Initialisierung A y B um einen Zustand zu validieren.

0voto

ChrisH Punkte 4728

Wenn einige der Felder niemals null sein dürfen, würden Sie sie normalerweise zu Parametern für den Klassenkonstruktor machen. Wenn Sie nicht immer alle erforderlichen Werte auf einmal zur Verfügung haben, 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 Entitätsklassen in einem angemessenen Umfang zu fixieren und gleichzeitig einen flexiblen Konstruktionsprozess zu ermöglichen. Es öffnet auch die Tür, um den Konstruktionsprozess mit anderen Implementierungen anzupassen, ohne den Entitätsklassenvertrag zu ändern.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X