Sind trivial geschützte Getter ein eklatanter Overkill?

8

Etwas, worüber ich vorher wirklich nicht nachgedacht habe (AS3-Syntax):

private var m_obj:Object;
protected function get obj():Object
{
    return m_obj;
}

private var m_str:String;
protected function get str():String
{
    return m_str;
}

Zumindest Unterklassen können m_obj oder m_str nicht setzen (obwohl sie m_obj noch ändern könnten).

Meine Frage: Ist das nur eklatanter Overkill?


Die Frage dieses Programmierers: Wann oder warum sollte man Getter / Setter für Klasseneigenschaften verwenden, anstatt sie einfach zu öffentlichen Eigenschaften zu machen? wurde als Duplikat vorgeschlagen.

Diese Frage ist anders, da sie nur öffentliche oder private Eigenschaften betrifft und ob eine Eigenschaft mit Getter & Setter umwickelt werden soll. Bei meiner Frage konzentriere ich mich auf geschützte Variablen und darauf, wie erbende Klassen mit diesen Variablen interagieren würden.

Die alternative Implementierung wäre also:

protected var m_obj:Object; //more accessible than a private variable with a protected getter
protected var m_str:String; //more accessible than a private variable with a protected getter

Meine Frage ist ähnlich, weil ich über die Verwendung trivial geschützter Getter spreche, um das Schreiben von Unterklassen in die Variablen zu blockieren , ihnen aber allen anderen Zugriff zu ermöglichen.
In Sprachen wie AS3 können sie sogar Änderungen an veränderlichen Objektvariablen vornehmen, solange die Referenzen selbst nicht geändert werden.

Panzerkrise
quelle
2
Frag dich selbst; ist es gefährlich, dass Unterklassen es auf etwas Beliebiges setzen können, wenn die Antwort ja ist, dann ist es nicht übertrieben
Ratschenfreak
1
Ich habe die Frage bearbeitet, um klarer zu machen, wovon ich spreche, nicht stattdessen zu tun.
Panzercrisis

Antworten:

6

Ist das nur eklatanter Overkill?

Oft ist es das, manchmal nicht.

Wenn Sie sich m_objin einem bekannten guten Zustand befinden, wird das Liskov-Substitutionsprinzip geschützt, und Ihr Code bleibt widerstandsfähiger und von höherer Qualität. Manchmal können Sie darauf vertrauen, dass Erben dieses Verhalten respektieren, manchmal nicht (entweder aufgrund des Nutzungsmusters oder der Subtilität des Vertrags, den es implementiert). Obwohl dieser Code / diese Frage auch auf einige der Gründe für "Warum schlägt Clean Code vor, geschützte Variablen zu vermeiden?" Stolpert.

Telastyn
quelle
Lohnt es sich manchmal, wenn die Unterklassen noch fast alles tun können, was sie wollen, außer es auf null oder eine andere Instanz zu setzen?
Panzercrisis
@Panzercrisis - es kommt auf die Sprache an. In nicht speicherverwalteten Sprachen ist es eine große Sache, sie auf null / etwas anderes zu setzen.
Telastyn
13

Trivial geschützte Getter haben mindestens zwei praktische Vorteile gegenüber einer exponierten Variablen:

  • Zum Debuggen sind sie der perfekte Ort, um einen bedingten Haltepunkt zu setzen, der ausgelöst wird, wenn „dieser lustige Wert in Ihr Programm gelangt“. Es ist viel schwieriger, dasselbe Steuerelement zu erhalten, wenn Sie direkt auf den Wert zugreifen.

  • Zum Verspotten eines Objekts, wenn Sie diese Methode zum Testen von Nachkommen verwenden.

Die Kosten für die Verwendung von Gettern sind praktisch Null, daher gibt es keinen wirklichen Grund, sie nicht zu verwenden. Gibt es?

Michael Le Barbier Grünewald
quelle
Vielen Dank. Die Frage wurde jedoch aus Gründen der Klarheit bearbeitet.
Panzercrisis
Meine Antwort bezieht sich nicht auf das publicoder protectedAttribut des Getters! :-)
Michael Le Barbier Grünewald
Oh, tut mir leid.
Panzercrisis
4
Die Kosten sind im Code aufgebläht. Die Kosten sind nicht hoch, aber größer als Null. Sicher, die Getter sind trivial, aber sie müssen jedes Mal übergangen werden, wenn jemand an der Klasse arbeitet. Und sie können falsch sein: get x() ... { return _y; } Niemand würde das jemals tippen, aber es könnte aufgrund eines falschen Ausschneidens und Einfügens passieren.
Kevin Cline
3
Es ist jedoch ein Kompromiss zwischen verschiedenen Kosten. Und meiner Erfahrung nach sind die Kosten für das Aufspüren von Fehlern, bei denen Unterklassen ihre Elternklasse auf subtile Weise gefährden, im Vergleich zu den Kosten für die Wartung von Gettern und Setzern enorm.
Julia Hayward
7

Unabhängig davon, ob Sie Getter oder Feldzugriff verwenden, wird jeder erfahrene Prüfer das Problem in Ihrem Entwurf herausfinden und sich darüber beschweren, dass Ihre Objekte Daten anstelle von Verhalten verfügbar machen.

Beachten Sie, dass das Ansprechen von "geschützt" nicht hilft, da der Zweck der Vererbung darin besteht, das Anpassen / Einstellen des Verhaltens des übergeordneten Elements ( LSP ) durch Unterklassen zu ermöglichen und nicht das wahllose Durcheinander mit übergeordneten Daten zu rechtfertigen. Wie Bertrand Meyer es ausdrückt,

Weder das Open-Closed-Prinzip noch die Neudefinition der Vererbung sind eine Möglichkeit, Konstruktionsfehler zu beheben ... (Die einzige mögliche Ausnahme für diese Regel ist der Fall fehlerhafter Software, die Sie nicht ändern können.)

Die Argumentation "Getter sind böse" gilt unabhängig davon, ob Sie getexplizit buchstabieren oder sie hinter dem Feldzugriff maskieren.

siehe diesen Artikel oder diesen Artikel von Alan Holub ...

Sie sollten keine Accessor-Methoden (Getter und Setter) verwenden, es sei denn, dies ist unbedingt erforderlich, da diese Methoden Informationen über die Implementierung einer Klasse enthalten und folglich die Wartung Ihres Codes erschweren. Manchmal sind get / set-Methoden unvermeidlich, aber ein erfahrener OO-Designer könnte wahrscheinlich 99 Prozent der derzeit in Ihrem Code enthaltenen Accessoren ohne große Schwierigkeiten entfernen.

Getter / Setter-Methoden finden häufig ihren Weg im Code, weil der Codierer prozedural dachte. Der beste Weg, um aus dieser prozeduralen Denkweise auszubrechen, besteht darin, in einem Gespräch zwischen Objekten zu denken, die genau definierte Verantwortlichkeiten haben ...

Wenn Sie ernsthaft über das Problem besorgt sind, sollten Sie zunächst den Code neu gestalten , um sowohl Getter als auch nicht-privaten Feldzugriff zu vermeiden , anstatt erfolglose Gedankenspiele zu spielen, bei denen der am wenigsten unangemessene Weg ausgewählt wird .

Es lohnt sich noch einmal zu betonen, dass das Problem durch das Verstecken von problematischem Design hinter einer "geschützten" Nebelwand nicht behoben werden kann - Vererbung rechtfertigt solche Tricks nicht. Wenn Sie Ihre Frage umschreiben, bemühen Sie sich, die Dinge so zu gestalten, dass geerbte Klassen überhaupt nicht mit diesen Variablen interagieren - weder über Getter noch über den Feldzugriff. Sagen Sie, fragen Sie nicht :

Der Prozedurcode erhält Informationen und trifft dann Entscheidungen. Objektorientierter Code weist Objekte an, Dinge zu tun.

Ich verbringe Zeit und Mühe damit, zu entscheiden, wie ich lieber Informationen erhalten möchte, und empfehle gegen das klassische Prinzip, dies insgesamt zu vermeiden. Das klingt für mich jetzt nach einem offensichtlichen Overkill .

Mücke
quelle
Wir sind von der gleichen Schule. Die Frage sollte nicht lauten: "Oh, die Basisklasse funktioniert nicht richtig. Wie kann ich mit ihren Daten herumspielen?" Es sollte lauten: "Wie kann ich die Basisklasse zum Laufen bringen, damit ich mich nicht anlegen muss?" mit seinem inneren Zustand? " Selten implementiere ich Getter. Fast nie implementiere ich Setter. Die Methoden in meinen Klassen sind Befehle im Stil von "Tell Don't Ask". Manchmal habe ich das Gefühl, dass eine meiner Hauptaufgaben darin besteht, Fehler zu beheben, indem Implementierungsdetails ausgeblendet werden, die von faulen oder gedankenlosen Programmierern angezeigt werden.
Dash-Tom-Bang
1

Im Falle einer unveränderlichen Klassenimplementierung würde das Schützen Ihrer internen Variablen den von Ihnen festgelegten Vertrag brechen. In diesem Fall wären die geschützten Getter nicht übertrieben und notwendig (obwohl Sie Kopien Ihrer Variablen zurückgeben müssten, keinen Zeiger darauf ).

In veränderlichen Klassen ziehe ich es immer noch vor, Getter / Setter dem direkten Zugriff auf meine internen Variablen zu gewähren. Auf diese Weise können Sie den Umgang mit Ihren internen Variablen nach Belieben ändern, ohne befürchten zu müssen, dass Clientcode beschädigt wird. Stellen Sie sich zum Beispiel vor, Sie haben eine Java-Klasse wie folgt:

public abstract class SomeClass {
    protected int[] someVariable;

    // Rest of implementation
}

Die Erweiterung der Client-Klasse SomeClasswürde dann direkt darauf zugreifen someVariableund sie nach eigenem Ermessen bearbeiten. Dies kann nun zu allen möglichen Problemen führen, wenn diese Klassen nicht someVariablein einem akzeptablen Zustand bleiben. Außerdem, wenn Sie in einer späteren Version entscheiden, die Definition zu ändern , SomeClasseine verwenden , List<Integers>anstelle einem Arrays, werden Sie dann brechen Kompatibilität mit der bestehenden Code - Basis, die erwarten , dass SomeClassein internes Array von haben ints, keine Liste ints.

Wenn Sie in diesem Fall geschützte Getter verwenden, können Sie diese Änderung vornehmen, ohne einen zuvor festgelegten Vertrag zu brechen.

public abstract class SomeClass {
    List<Integer> someVariable;

    protected int[] getSomeVariable () {
        // Return int[] version of someVariable
    }

    // Rest of implementation
}
Laf
quelle