Frage Warum verwenden Sie Getter und Setter / Accessoren? [geschlossen]


Was ist der Vorteil der Verwendung von Getter und Setter - die nur erhalten und gesetzt werden - anstatt einfach öffentliche Felder für diese Variablen zu verwenden?

Wenn Getter und Setter jemals mehr tun als nur das einfache Get / Set, kann ich das sehr schnell herausfinden, aber ich weiß nicht 100%, wie:

public String foo;

ist schlimmer als:

private String foo;
public void setFoo(String foo) { this.foo = foo; }
public String getFoo() { return foo; }

Während Ersteres viel weniger Vorabcode benötigt.


1303
2017-10-14 18:20


Ursprung


Antworten:


Es gibt tatsächlich viele gute Gründe In Betracht ziehen, Accessoren zu verwenden anstatt Felder einer Klasse direkt zu exponieren - über das Argument der Verkapselung hinaus und zukünftige Änderungen einfacher zu machen.

Hier sind einige der Gründe, die mir bekannt sind:

  • Einkapselung des Verhaltens, das mit dem Abrufen oder Festlegen der Eigenschaft verbunden ist - dies ermöglicht es, zusätzliche Funktionalität (wie Validierung) später hinzuzufügen.
  • Verbergen der internen Repräsentation der Eigenschaft, während eine Eigenschaft mithilfe einer alternativen Repräsentation verfügbar gemacht wird.
  • Isolieren Sie Ihre öffentliche Schnittstelle vor Änderungen - dadurch bleibt die öffentliche Schnittstelle konstant, während sich die Implementierung ändert, ohne bestehende Kunden zu beeinträchtigen.
  • Steuern der Lebensdauer- und Speichermanagement-Semantik der Eigenschaft - besonders wichtig in nicht verwalteten Speicherumgebungen (wie C ++ oder Objective-C).
  • Bereitstellen eines Debugging-Abfangpunkts für den Fall, dass sich eine Eigenschaft zur Laufzeit ändert - das Debuggen, wann und wo eine Eigenschaft auf einen bestimmten Wert geändert wurde, kann in einigen Sprachen ziemlich schwierig sein.
  • Verbesserte Interoperabilität mit Bibliotheken, die für den Umgang mit Property Getter / Setter entwickelt wurden - Mocking, Serialisierung und WPF kommen in den Sinn.
  • Erlauben von Vererbern, die Semantik der Verhaltensweise der Eigenschaft zu ändern, indem sie die Getter / Setter-Methoden überschreiben.
  • Zulassen, dass der Getter / Setter als Lambda-Ausdrücke und nicht als Werte weitergegeben wird.
  • Getter und Setter können unterschiedliche Zugriffsebenen zulassen - beispielsweise kann der Aufruf öffentlich sein, aber der Satz könnte geschützt werden.

794
2017-10-14 18:47



Weil 2 Wochen (Monate, Jahre) von jetzt an, wenn Sie erkennen, dass Ihr Setter tun muss Mehr Anstatt nur den Wert festzulegen, merkt man auch, dass die Immobilie direkt in 238 anderen Klassen verwendet wurde :-)


389
2017-10-14 18:23



Ein öffentliches Feld ist nicht schlechter als ein Getter / Setter-Paar, das nichts anderes tut, als das Feld zurückzugeben und ihm zuzuordnen. Erstens ist klar, dass es (in den meisten Sprachen) keinen funktionalen Unterschied gibt. Jeder Unterschied muss in anderen Faktoren wie Wartbarkeit oder Lesbarkeit bestehen.

Ein oft erwähnter Vorteil von Getter / Setter-Paaren ist nicht. Es gibt diesen Anspruch, dass Sie die Implementierung ändern können und Ihre Clients nicht neu kompiliert werden müssen. Angeblich können Sie später Funktionalität hinzufügen, wie zB Validierung, und Ihre Kunden müssen nicht einmal darüber Bescheid wissen. Das Hinzufügen von Validierung zu einem Setter ist jedoch eine Änderung seiner Vorbedingungen, ein Verstoß gegen den vorherigen Vertrag, das war ganz einfach: "Sie können alles hier hineinstecken, und Sie können das gleiche später vom Getter bekommen".

Nun, da Sie den Vertrag gebrochen haben, sollten Sie jede Datei in der Codebasis ändern, was Sie nicht vermeiden sollten. Wenn Sie es vermeiden, machen Sie die Annahme, dass der gesamte Code davon ausgeht, dass der Vertrag für diese Methoden anders ist.

Wenn dies nicht der Vertrag gewesen wäre, dann erlaubte die Schnittstelle den Clients, das Objekt in ungültige Zustände zu versetzen. Das ist das genaue Gegenteil der Kapselung Wenn dieses Feld von Anfang an nicht wirklich auf etwas eingestellt werden konnte, warum war die Validierung dann nicht von Anfang an da?

Das gleiche Argument gilt für andere vermeintliche Vorteile dieser Pass-Through-Getter / Setter-Paare: Wenn Sie später entscheiden, den Wert zu ändern, der gesetzt wird, brechen Sie den Vertrag. Wenn Sie die Standardfunktionalität in einer abgeleiteten Klasse außer einigen wenigen harmlosen Änderungen (wie Protokollierung oder anderes nicht beobachtbares Verhalten) überschreiben, brechen Sie den Vertrag der Basisklasse. Dies ist eine Verletzung des Liskow-Substituierbarkeitsprinzips, das als einer der Grundsätze von OO angesehen wird.

Wenn eine Klasse diese dummen Getter und Setter für jedes Feld hat, dann ist es eine Klasse, die überhaupt keine Invarianten hat, Kein Vertrag. Ist das wirklich objektorientiertes Design? Wenn die ganze Klasse diese Getter und Setter hat, ist es nur ein blöder Datenhalter, und dumme Datenhalter sollten wie dumme Datenhalter aussehen:

class Foo {
public:
    int DaysLeft;
    int ContestantNumber;
};

Das Hinzufügen von Pass-Through-Getter / Setter-Paaren zu einer solchen Klasse fügt keinen Wert hinzu. Andere Klassen sollten sinnvolle Operationen bereitstellen, nicht nur Operationen, die bereits von Feldern bereitgestellt werden. So können Sie nützliche Invarianten definieren und pflegen.

Klient: "Was kann ich mit einem Objekt dieser Klasse tun?"
Designer: "Sie können mehrere Variablen lesen und schreiben."
Klient: "Oh ... cool, denke ich?"

Es gibt Gründe, Getter und Setter zu verwenden, aber wenn diese Gründe nicht existieren, ist das Erstellen von Getter / Setter-Paaren im Namen von falschen Verkapselungsgöttern keine gute Sache. Gültige Gründe dafür, dass Getter oder Setter die Dinge, die oft erwähnt werden, als potenzielle Änderungen, die Sie später vornehmen können, wie die Validierung oder verschiedene interne Repräsentationen, enthalten. Oder vielleicht sollte der Wert für Clients lesbar sein, aber nicht beschreibbar sein (zum Beispiel das Lesen der Größe eines Wörterbuchs), so dass ein einfacher Getter eine gute Wahl ist. Aber diese Gründe sollten vorhanden sein, wenn Sie die Wahl treffen, und nicht nur als eine mögliche Sache, die Sie später vielleicht wollen. Dies ist eine Instanz von YAGNI (Du wirst es nicht brauchen).


307
2017-08-24 10:55



Viele Leute reden über die Vorteile von Getter und Setter, aber ich möchte den Advokaten des Teufels spielen. Im Moment debugge ich ein sehr großes Programm, wo die Programmierer beschlossen haben, alles zu tun, Getters und Setter. Das mag nett erscheinen, aber es ist ein Reverse-Engineering-Albtraum.

Angenommen, Sie durchsuchen Hunderte von Codezeilen, und Sie stoßen auf Folgendes:

person.name = "Joe";

Es ist ein wunderbar einfaches Stück Code, bis du erkennst, dass es ein Setter ist. Jetzt folgen Sie diesem Setter und stellen fest, dass er auch person.firstName, person.lastName, person.isHuman, person.hasReallyCommonFirstName setzt und person.update () aufruft, das eine Abfrage an die Datenbank usw. sendet. Oh, das ist wo dein Speicherleck aufgetreten ist.

Ein lokales Stück Code auf den ersten Blick zu verstehen, ist eine wichtige Eigenschaft guter Lesbarkeit, die Getter und Setter brechen. Deshalb versuche ich, sie zu vermeiden, wenn ich kann, und zu minimieren, was sie tun, wenn ich sie benutze.


75
2017-10-14 21:00



Es gibt viele Gründe. Mein Favorit ist, wenn Sie das Verhalten ändern oder regeln müssen, was Sie für eine Variable festlegen können. Nehmen wir zum Beispiel an, Sie hätten eine Methode setSpeed ​​(int speed). Aber Sie wollen, dass Sie nur eine maximale Geschwindigkeit von 100 einstellen können. Sie würden etwas wie tun:

public void setSpeed(int speed) {
  if ( speed > 100 ) {
    this.speed = 100;
  } else {
    this.speed = speed;
  }
}

Was nun, wenn Sie überall in Ihrem Code das öffentliche Feld verwenden und dann feststellen, dass Sie die obige Anforderung benötigen? Viel Spaß beim Aufspüren jeder Nutzung des öffentlichen Feldes, anstatt nur den Setter zu verändern.

Meine 2 Cent :)


43
2017-10-14 18:27



In einer reinen objektorientierten Welt ist Getter und Setter ein schreckliches Anti-Muster. Lesen Sie diesen Artikel: Getter / Setzer. Böse. Zeitraum. Kurz gesagt, sie ermutigen Programmierer, über Objekte als Datenstrukturen nachzudenken, und diese Art des Denkens ist rein prozedural (wie in COBOL oder C). In einer objektorientierten Sprache gibt es keine Datenstrukturen, sondern nur Objekte, die das Verhalten offenlegen (nicht Attribute / Eigenschaften!)

Sie können mehr über sie in Abschnitt 3.5 von finden Elegante Objekte (Mein Buch über objektorientierte Programmierung).


43
2017-09-23 16:39



Ein Vorteil von Accessoren und Mutatoren ist, dass Sie eine Validierung durchführen können.

Zum Beispiel, wenn foo war öffentlich, ich konnte es leicht einstellen null und dann könnte jemand anders versuchen, eine Methode für das Objekt aufzurufen. Aber es ist nicht mehr da! Mit einem setFoo Methode konnte ich das sicherstellen foo wurde nie eingestellt null.

Accessoren und Mutatoren ermöglichen auch die Kapselung - wenn Sie den Wert nicht einmal sehen sollen (vielleicht wird er im Konstruktor gesetzt und dann von Methoden verwendet, aber niemals geändert), wird er niemals von jemandem gesehen. Wenn Sie jedoch zulassen können, dass andere Klassen es sehen oder ändern, können Sie den richtigen Accessor und / oder Mutator bereitstellen.


36
2017-10-14 18:25



Hängt von deiner Sprache ab. Sie haben dieses "objektorientierte" anstatt "Java" markiert, deshalb möchte ich darauf hinweisen, dass die Antwort von ChssPly76 sprachabhängig ist. In Python gibt es beispielsweise keinen Grund, Getter und Setter zu verwenden. Wenn Sie das Verhalten ändern müssen, können Sie eine Eigenschaft verwenden, die einen Getter und Setter um den grundlegenden Attributzerhalt umschließt. Etwas wie das:

class Simple(object):
   def _get_value(self):
       return self._value -1

   def _set_value(self, new_value):
       self._value = new_value + 1

   def _del_value(self):
       self.old_values.append(self._value)
       del self._value

   value = property(_get_value, _set_value, _del_value)

28
2017-10-14 18:32



Nun, ich möchte nur hinzufügen, dass, selbst wenn sie manchmal für die Kapselung und Sicherheit Ihrer Variablen / Objekte notwendig sind, wir ein echtes objektorientiertes Programm programmieren müssen BEENDEN SIE DIE ZUGRIFFSTOPPEN NICHTManchmal hängen wir sehr von ihnen ab, wenn es nicht wirklich notwendig ist, und das macht fast dasselbe, als ob wir die Variablen öffentlich machen würden.


23
2017-08-22 14:47



Ich weiß, dass es ein bisschen spät ist, aber ich denke, dass es einige Leute gibt, die sich für Performance interessieren.

Ich habe einen kleinen Leistungstest gemacht. Ich habe eine Klasse "NumberHolder" geschrieben, die einen Integer enthält. Sie können diesen Integer entweder mit der Getter-Methode lesen anInstance.getNumber()oder durch direkten Zugriff auf die Nummer mit anInstance.number. Mein Programm liest die Nummer 1.000.000.000 Mal, auf beiden Wegen. Dieser Vorgang wird fünfmal wiederholt und die Zeit wird gedruckt. Ich habe folgendes Ergebnis:

Time 1: 953ms, Time 2: 741ms
Time 1: 655ms, Time 2: 743ms
Time 1: 656ms, Time 2: 634ms
Time 1: 637ms, Time 2: 629ms
Time 1: 633ms, Time 2: 625ms

(Zeit 1 ist der direkte Weg, Zeit 2 ist der Getter)

Du siehst, der Getter ist (fast) immer ein bisschen schneller. Dann habe ich es mit verschiedenen Zyklen versucht. Statt 1 Million habe ich 10 Millionen und 0,1 Millionen verwendet. Die Ergebnisse:

10 Millionen Zyklen:

Time 1: 6382ms, Time 2: 6351ms
Time 1: 6363ms, Time 2: 6351ms
Time 1: 6350ms, Time 2: 6363ms
Time 1: 6353ms, Time 2: 6357ms
Time 1: 6348ms, Time 2: 6354ms

Mit 10 Millionen Zyklen sind die Zeiten fast gleich. Hier sind 100 Tausend (0,1 Millionen) Zyklen:

Time 1: 77ms, Time 2: 73ms
Time 1: 94ms, Time 2: 65ms
Time 1: 67ms, Time 2: 63ms
Time 1: 65ms, Time 2: 65ms
Time 1: 66ms, Time 2: 63ms

Auch bei unterschiedlichen Zyklen ist der Getter etwas schneller als der normale Weg. Ich hoffe, das hat dir geholfen.


22
2017-08-30 18:59