Warum Getter und Setter / Accessoren verwenden?


1541

Was ist der Vorteil der Verwendung von Gettern und Setzern - die nur erhalten und setzen - anstatt einfach öffentliche Felder für diese Variablen zu verwenden?

Wenn Getter und Setter jemals mehr als nur das einfache Get / Set machen, kann ich dies sehr schnell herausfinden, aber ich bin nicht 100% klar, 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 der erstere viel weniger Boilerplate-Code benötigt.


6
@ Dean J: Duplizieren Sie mit vielen anderen Fragen: stackoverflow.com/search?q=getters+setters
Asaph

8
Natürlich sind beide gleich schlecht, wenn für das Objekt keine Eigenschaft geändert werden muss. Ich würde lieber alles privat machen und dann Getter hinzufügen, wenn es nützlich ist, und Setter, wenn nötig.
Tordek

26
Google "Accessoren sind böse"
OMG Ponys

34
"Accessoren sind böse", wenn Sie zufällig Funktionscode oder unveränderliche Objekte schreiben. Wenn Sie zufällig Stateful Mutable-Objekte schreiben, sind diese ziemlich wichtig.
Christian Hayter

Antworten:


980

Es gibt tatsächlich viele gute Gründe , die Verwendung von Accessoren in Betracht zu ziehen, anstatt Felder einer Klasse direkt verfügbar zu machen - über das Argument der Kapselung hinaus und um zukünftige Änderungen zu vereinfachen.

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

  • Kapselung des Verhaltens beim Abrufen oder Festlegen der Eigenschaft - Dadurch können zusätzliche Funktionen (wie die Validierung) später einfacher hinzugefügt werden.
  • Ausblenden der internen Darstellung der Eigenschaft beim Anzeigen einer Eigenschaft mithilfe einer alternativen Darstellung.
  • Isolieren Sie Ihre öffentliche Schnittstelle vor Änderungen - So bleibt die öffentliche Schnittstelle konstant, während sich die Implementierung ändert, ohne die vorhandenen Verbraucher zu beeinträchtigen.
  • Steuern der Lebensdauer und der Semantik der Speicherverwaltung (Entsorgung) der Eigenschaft - besonders wichtig in nicht verwalteten Speicherumgebungen (wie C ++ oder Objective-C).
  • Bereitstellen eines Abfangpunkts für das Debuggen, wenn sich eine Eigenschaft zur Laufzeit ändert - Das Debuggen, wann und wo sich eine Eigenschaft auf einen bestimmten Wert geändert hat, kann in einigen Sprachen ohne dies sehr schwierig sein.
  • Verbesserte Interoperabilität mit Bibliotheken, die für den Betrieb gegen Property Getter / Setter ausgelegt sind - Mocking, Serialization und WPF kommen in den Sinn.
  • Ermöglichen, dass Erben die Semantik des Verhaltens und der Offenlegung der Eigenschaft ändern, indem die Getter / Setter-Methoden überschrieben werden.
  • Ermöglichen, dass der Getter / Setter als Lambda-Ausdrücke und nicht als Werte weitergegeben wird.
  • Getter und Setter können unterschiedliche Zugriffsebenen zulassen - zum Beispiel kann das Get öffentlich sein, aber das Set kann geschützt werden.

59
+1. Nur um hinzuzufügen: Faules Laden zulassen. Kopieren beim Schreiben zulassen.
NewbiZ

6
@bjarkef: Ich glaube, publicAccessor-Methoden verursachen Codeduplizierungen und machen Informationen verfügbar. Öffentliche Accessoren sind für das Marshalling (Serialisierung) nicht erforderlich. In einigen Sprachen (Java) können publicGet-Accessoren den Rückgabetyp nicht ändern, ohne abhängige Klassen zu beschädigen. Darüber hinaus glaube ich, dass sie den OO-Prinzipien zuwiderlaufen. Siehe auch: stackoverflow.com/a/1462424/59087
Dave Jarvis

15
@sbi: Eine Sache, die mit einem Eigenschaftssetzer gekauft wird, selbst in einem gut gestalteten Framework, ist die Möglichkeit, dass ein Objekt ein anderes benachrichtigt, wenn sich eine Eigenschaft ändert.
Supercat

22
@supercat: Allerdings mache ich im Allgemeinen keine Werbung für öffentliche Daten. Die Benachrichtigung anderer Objekte kann genauso gut über reale Methoden erfolgen, die eine signifikante Abstraktion auf einem bloßen Datencontainer mit (mehr oder weniger) öffentlichen Datenfeldern liefern. Stattdessen plane.turnTo(dir); plane.setSpeed(spd); plane.setTargetAltitude(alt); plane.getBreaks().release();möchte ich sagen plane.takeOff(alt). Welche inneren Datenfelder geändert werden müssen, um die Ebene in den takingOffModus zu versetzen, geht mich nichts an. Und welche anderen Objekte ( breaks) die Methode benachrichtigt, möchte ich auch nicht wissen.
sbi

8
@ BenLee: Ich weiß wirklich nicht, wie ich es sonst sagen soll. "Accessors" ist nur ein Synonym für "Getter / Setter", und in meinen ersten beiden Kommentaren habe ich erklärt, warum dies ein Missbrauch des Begriffs "OOP" ist. Wenn Sie darauf zugreifen und den Status direkt manipulieren müssen, ist dies kein Objekt im OOP-Sinne dieses Begriffs.
sbi

480

Da Sie in 2 Wochen (Monaten, Jahren) feststellen, dass Ihr Setter mehr als nur den Wert festlegen muss, werden Sie auch feststellen, dass die Eigenschaft direkt in 238 anderen Klassen verwendet wurde :-)


84
Ich sitze und starre auf eine 500k Line App, in der sie nie gebraucht wurde. Das heißt, wenn es einmal benötigt wird, würde es einen Wartungsalptraum verursachen. Gut genug für ein Häkchen für mich.
Dean J

20
Ich kann Sie und Ihre App nur beneiden :-) Das heißt, es hängt auch wirklich von Ihrem Software-Stack ab. Mit Delphi zum Beispiel (und C # - glaube ich?) Können Sie Eigenschaften als Bürger erster Klasse definieren, bei denen sie ein Feld zunächst direkt lesen / schreiben können, dies jedoch - falls erforderlich - auch über Getter / Setter-Methoden. Mucho bequem. Java leider nicht - ganz zu schweigen vom Javabeans-Standard, der Sie dazu zwingt, auch Getter / Setter zu verwenden.
ChssPly76

14
Während dies in der Tat ein guter Grund für die Verwendung von Accessoren ist, bieten viele Programmierumgebungen und Editoren jetzt Unterstützung für das Refactoring (entweder in der IDE oder als kostenlose Add-Ins), wodurch die Auswirkungen des Problems etwas verringert werden.
LBushkin

62
klingt nach
vorzeitiger

41
Die Frage, die Sie stellen müssen, wenn Sie sich fragen, ob Sie Getter und Setter implementieren sollen, lautet: Warum sollten Benutzer einer Klasse überhaupt auf die Innereien der Klasse zugreifen müssen? Es spielt keine Rolle, ob sie dies direkt tun oder durch eine dünne Pseudoschicht geschützt sind. Wenn Benutzer auf Implementierungsdetails zugreifen müssen, ist dies ein Zeichen dafür, dass die Klasse nicht genug Abstraktion bietet. Siehe auch diesen Kommentar .
sbi

357

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

Ein oft genannter Vorteil von Getter / Setter-Paaren ist dies nicht. Es gibt diese Behauptung, dass Sie die Implementierung ändern können und Ihre Clients nicht neu kompiliert werden müssen. Angeblich können Sie mit Setzern später Funktionen wie die Validierung hinzufügen, und Ihre Kunden müssen nicht einmal darüber Bescheid wissen. Das Hinzufügen einer Validierung zu einem Setter ist jedoch eine Änderung seiner Voraussetzungen, eine Verletzung des vorherigen Vertrags , die ganz einfach lautete: "Sie können hier alles einfügen und das Gleiche später vom Getter erhalten".

Nachdem Sie den Vertrag gebrochen haben, sollten Sie jede Datei in der Codebasis ändern und nicht vermeiden. Wenn Sie dies vermeiden, gehen Sie davon aus, dass der gesamte Code davon ausgegangen ist, dass der Vertrag für diese Methoden unterschiedlich war.

Wenn dies nicht der Vertrag gewesen sein sollte, erlaubte die Schnittstelle den Clients, das Objekt in einen ungültigen Zustand zu versetzen. Das ist das genaue Gegenteil von Kapselung. Wenn dieses Feld von Anfang an nicht wirklich auf irgendetwas gesetzt werden konnte, warum war die Validierung dort nicht von Anfang an?

Das gleiche Argument gilt für andere vermeintliche Vorteile dieser Pass-Through-Getter / Setter-Paare: Wenn Sie später entscheiden, den eingestellten Wert zu ändern, brechen Sie den Vertrag. Wenn Sie die Standardfunktionalität in einer abgeleiteten Klasse überschreiben, und zwar über einige harmlose Änderungen (wie Protokollierung oder anderes nicht beobachtbares Verhalten) hinaus, brechen Sie den Vertrag der Basisklasse. Dies ist ein Verstoß gegen das Liskov-Substitutability-Prinzip, 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, keinen Vertrag . Ist das wirklich objektorientiertes Design? Wenn die Klasse nur diese Getter und Setter hat, ist es nur ein dummer 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 bringt keinen Wert. Andere Klassen sollten sinnvolle Operationen bereitstellen, nicht nur Operationen, die Felder bereits bereitstellen. So können Sie nützliche Invarianten definieren und pflegen.

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

Es gibt Gründe, Getter und Setter zu verwenden, aber wenn diese Gründe nicht existieren, ist es keine gute Sache, Getter / Setter-Paare im Namen falscher Verkapselungsgötter zu bilden. Zu den gültigen Gründen, um Getter oder Setter zu erstellen, gehören die Dinge, die häufig als mögliche Änderungen erwähnt werden, die Sie später vornehmen können, z. B. die Validierung oder andere interne Darstellungen. Oder vielleicht sollte der Wert für Clients lesbar, aber nicht beschreibbar sein (z. B. das Lesen der Größe eines Wörterbuchs), sodass ein einfacher Getter eine gute Wahl ist. Aber diese Gründe sollten da sein, wenn Sie die Wahl treffen, und nicht nur als potenzielle Sache, die Sie später vielleicht wollen. Dies ist eine Instanz von YAGNI ( Du wirst es nicht brauchen ).


12
Tolle Antwort (+1). Mein einziger Kritikpunkt ist, dass ich mehrere Male gelesen habe, um herauszufinden, wie sich "Validierung" im letzten Absatz von "Validierung" in den ersten paar unterscheidet (die Sie im letzteren Fall verworfen, aber im ersteren beworben haben). Eine Anpassung des Wortlauts könnte in dieser Hinsicht hilfreich sein.
Leichtigkeitsrennen im Orbit

14
Dies ist eine großartige Antwort, aber leider hat die aktuelle Ära vergessen, was "Verstecken von Informationen" ist oder wofür es ist. Sie haben nie über Unveränderlichkeit gelesen und auf ihrer Suche nach Agilität nie das Zustandsübergangsdiagramm gezeichnet, das definiert, wie die Rechtszustände eines Objekts sind und was nicht.
Darrell Teague

2
Eine wichtige Beobachtung ist, dass Getter viel nützlicher sind als Setter. Ein Getter kann einen berechneten Wert oder einen zwischengespeicherten berechneten Wert zurückgeben. Alles, was ein Setter tun kann, ist eine Validierung und dann das privateFeld zu ändern . Wenn eine Klasse keine Setter hat, ist es einfach, sie unveränderlich zu machen.
Raedwald

7
Ähm, ich denke du weißt nicht, was eine Klasseninvariante ist. Wenn es sich um eine nicht triviale Struktur handelt, bedeutet dies so ziemlich, dass Invarianten aufrechterhalten werden müssen, und man kann sie einfach nicht ohne diese entwerfen. "Es gab einen Fehler, ein Mitglied wurde falsch aktualisiert." liest auch so ziemlich wie "Ein Mitglieder-Update hat die Klasseninvarianten verletzt". Eine gut gestaltete Klasse erlaubt es dem Client-Code nicht, seine Invarianten zu verletzen.
R. Martinho Fernandes

5
+1 für 'dumme Dateninhaber sollten wie dumme Dateninhaber aussehen' Leider scheint heutzutage jeder alles um dumme Datenhalter herum zu entwerfen, und viele Frameworks erfordern es sogar ...
Joeri Hendrickx

92

Viele Leute sprechen über die Vorteile von Gettern und Setzern, aber ich möchte Devil's Advocate spielen. Im Moment debugge ich ein sehr großes Programm, in dem die Programmierer beschlossen haben, alles Getter und Setter zu machen. Das mag schön erscheinen, ist aber ein Alptraum für die Rückentwicklung.

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

person.name = "Joe";

Es ist ein wunderschön einfacher Code, bis Sie erkennen, dass es sich um einen Setter handelt. Nun folgen Sie diesem Setter und stellen fest, dass er auch person.firstName, person.lastName, person.isHuman, person.hasReallyCommonFirstName setzt und person.update () aufruft, wodurch eine Abfrage an die Datenbank usw. gesendet wird. Oh, das ist wo Ihr Speicherverlust aufgetreten ist.

Das Verständnis eines lokalen Codeteils auf den ersten Blick ist eine wichtige Eigenschaft für eine gute Lesbarkeit, die Getter und Setter häufig beschädigen. Deshalb versuche ich, sie zu vermeiden, wenn ich kann, und zu minimieren, was sie tun, wenn ich sie benutze.


8
Ja. Derzeit wird eine große Codebasis umgestaltet, und dies war ein Albtraum. Die Getter und Setter tun viel zu viel, einschließlich des Aufrufs anderer sehr beschäftigter Getter und Setter, die die Lesbarkeit auf nichts reduzieren. Die Validierung ist ein guter Grund für die Verwendung von Accessoren, aber wenn Sie sie verwenden, um viel mehr zu tun, scheint dies keinen potenziellen Nutzen zu bringen.
Fadecomic

31
Dies ist ein Argument gegen syntaktischen Zucker, nicht gegen Setter im Allgemeinen.
Phil

6
Richtig und wahr, aber darüber hinaus zeigt es auf, warum einzelne Eigenschaftssetzer die Tür für ungültige Zustände öffnen, ohne dass Abhilfe für die Integrität eines bestimmten Objekts geschaffen werden kann, durch das die Abstraktion auslaufen kann.
Darrell Teague

"Es ist ein wunderschön einfacher Code, bis man merkt, dass es ein Setter ist" - hey, war der ursprüngliche Beitrag über Java oder über C #? :)
Honza Zidek

In Bezug auf die Lesbarkeit stimme ich voll und ganz zu. Es ist völlig explizit, was passiert, wenn person.name = "Joe";es aufgerufen wird. Die Informationen sind nicht überfüllt, die Syntaxhervorhebung zeigt, dass es sich um ein Feld handelt, und das Refactoring ist einfacher (es ist nicht erforderlich, zwei Methoden und ein Feld zu refactorisieren). Zweitens können all diese verschwendeten Gehirnzyklen, die an "getIsValid ()" denken oder "isValid ()" usw. sein sollten, vergessen werden. Ich kann mir keine Single vorstellen, wenn ich von einem Getter / Setter vor einem Fehler gerettet wurde.
Will

53

In einer reinen objektorientierten Welt ist Getter und Setter ein schreckliches Anti-Muster . Lesen Sie diesen Artikel: Getter / Setter. 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 Verhalten offenlegen (keine Attribute / Eigenschaften!)

Weitere Informationen hierzu finden Sie in Abschnitt 3.5 von Elegant Objects (mein Buch über objektorientierte Programmierung).


7
Getter und Setter schlagen ein anämisches Domänenmodell vor.
Raedwald

7
Interessanter Standpunkt. In den meisten Programmierkontexten benötigen wir jedoch Datenstrukturen. Nehmen Sie das Beispiel "Hund" des verlinkten Artikels. Ja, Sie können das Gewicht eines echten Hundes nicht ändern, indem Sie ein Attribut festlegen ... aber a new Dog()ist kein Hund. Es ist ein Objekt, das Informationen über einen Hund enthält. Und für diese Verwendung ist es natürlich, ein falsch aufgezeichnetes Gewicht korrigieren zu können.
Stephen C

3
Nun, ich sage Ihnen, dass die meisten nützlichen Programme keine Objekte der realen Welt modellieren / simulieren müssen. IMO, hier geht es überhaupt nicht um Programmiersprachen. Es geht darum, wofür wir Programme schreiben.
Stephen C

3
Reale Welt oder nicht, yegor ist völlig richtig. Wenn das, was Sie haben, wirklich eine "Struktur" ist und Sie keinen Code schreiben müssen, der namentlich darauf verweist, fügen Sie ihn in eine Hashtabelle oder eine andere Datenstruktur ein. Wenn Sie Code dafür schreiben müssen, fügen Sie ihn als Mitglied einer Klasse ein und fügen Sie den Code, der diese Variable manipuliert, in dieselbe Klasse ein, und lassen Sie den Setter & Getter weg. PS. Obwohl ich den Standpunkt von yegor größtenteils teile, bin ich zu der Überzeugung gelangt, dass kommentierte Beans ohne Code etwas nützliche Datenstrukturen sind - manchmal sind auch Getter erforderlich, Setter sollten niemals existieren.
Bill K

1
Ich wurde mitgerissen - diese ganze Antwort, obwohl richtig und relevant, spricht die Frage nicht direkt an. Vielleicht sollte es heißen "Sowohl Setter / Getter als auch öffentliche Variablen sind falsch" ... Um genau zu sein, sollten Setter und beschreibbare öffentliche Variablen niemals verwendet werden, während Getter den öffentlichen endgültigen Variablen ziemlich ähnlich sind und gelegentlich ein notwendiges Übel sind aber keiner ist viel besser als der andere.
Bill K

52

Es gibt viele Gründe. Mein Favorit ist, wenn Sie das Verhalten ändern oder regulieren müssen, was Sie für eine Variable festlegen können. Angenommen, Sie hatten eine setSpeed-Methode (int speed). Sie möchten jedoch, dass Sie nur eine Höchstgeschwindigkeit von 100 einstellen können. Sie würden Folgendes tun:

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

Was wäre, wenn Sie ÜBERALL in Ihrem Code das öffentliche Feld verwenden und dann feststellen würden, dass Sie die oben genannte Anforderung benötigen? Viel Spaß beim Aufspüren jeder Nutzung des öffentlichen Feldes, anstatt nur Ihren Setter zu modifizieren.

Meine 2 Cent :)


61
Es sollte nicht so schwer sein, jede Nutzung des öffentlichen Feldes zu jagen. Machen Sie es privat und lassen Sie den Compiler sie finden.
Nathan Fellman

12
Das stimmt natürlich, aber warum sollte es schwieriger sein, als es gedacht war? Der Get / Set-Ansatz ist immer noch die bessere Antwort.
Hardryv

28
@Nathan: Die Suche nach anderen Verwendungen ist nicht das Problem. Sie alle zu ändern ist.
Graeme Perrow

22
@GraemePerrow muss sie alle ändern, ist ein Vorteil, kein Problem :( Was wäre, wenn Sie Code hätten, bei dem angenommen wird, dass die Geschwindigkeit höher als 100 sein könnte (denn Sie wissen, bevor Sie den Vertrag gebrochen haben, könnte es sein!) ( while(speed < 200) { do_something(); accelerate(); })
R. Martinho Fernandes

29
Es ist ein sehr schlechtes Beispiel! Jemand sollte anrufen: myCar.setSpeed(157);und nach ein paar Zeilen speed = myCar.getSpeed();Und jetzt ... Ich wünsche Ihnen viel Spaß speed==100beim Debuggen, während Sie versuchen zu verstehen, warum es sein sollte157
Piotr Aleksander Chmielowski

38

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

Wenn es beispielsweise fooöffentlich war, konnte ich es leicht einstellen nullund dann konnte jemand anderes versuchen, eine Methode für das Objekt aufzurufen. Aber es ist nicht mehr da! Mit einer setFooMethode konnte ich sicherstellen, dass dies foonie eingestellt wurde null.

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


28

Kommt auf deine Sprache an. Sie haben dieses "objektorientiert" und nicht "Java" markiert, daher 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 einen Setter um den grundlegenden Attributzugriff herum 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)

2
Ja, das habe ich in einem Kommentar unter meiner Antwort gesagt. Java ist nicht die einzige Sprache, die Getter / Setter als Krücke verwendet, genau wie Python nicht die einzige Sprache ist, die Eigenschaften definieren kann. Der Hauptpunkt bleibt jedoch weiterhin bestehen - "Eigentum" ist nicht dasselbe "öffentliche Feld".
ChssPly76

1
@jcd - überhaupt nicht. Sie definieren Ihre "Schnittstelle" (öffentliche API wäre hier ein besserer Begriff), indem Sie Ihre öffentlichen Felder verfügbar machen. Sobald das erledigt ist, gibt es kein Zurück mehr. Eigenschaften sind KEINE Felder, da sie Ihnen einen Mechanismus bieten, mit dem Sie versuchen können, auf Felder zuzugreifen (indem Sie sie an Methoden weiterleiten, sofern diese definiert sind). das ist jedoch nichts anderes als Syntaxzucker über Getter / Setter-Methoden. Es ist äußerst praktisch, ändert aber nichts am zugrunde liegenden Paradigma - das Freilegen von Feldern ohne Kontrolle über den Zugriff auf sie verstößt gegen das Prinzip der Kapselung.
ChssPly76

14
@ ChssPly76 - Ich bin anderer Meinung. Ich habe genauso viel Kontrolle, als wären sie Eigenschaften, weil ich sie jederzeit zu Eigenschaften machen kann. Es gibt keinen Unterschied zwischen einer Eigenschaft, die Boilerplate-Getter und -Setter verwendet, und einem Raw-Attribut, außer dass das Raw-Attribut schneller ist, da es die zugrunde liegende Sprache verwendet, anstatt Methoden aufzurufen. Funktionell sind sie identisch. Die Kapselung kann nur verletzt werden, wenn Sie der Meinung obj.set_attr('foo')sind, dass Klammern ( ) Gleichheitszeichen ( obj.attr = 'foo') von Natur aus überlegen sind . Öffentlicher Zugang ist öffentlicher Zugang.
JCDyer

1
@jcdyer so viel Kontrolle ja, aber nicht so viel Lesbarkeit, andere gehen oft fälschlicherweise davon aus, dass obj.attr = 'foo'nur die Variable gesetzt wird, ohne dass etwas anderes passiert
Timo Huovinen

9
@TimoHuovinen Wie unterscheidet sich das von einem Benutzer in Java, der davon ausgeht, dass obj.setAttr('foo')"nur die Variable gesetzt wird, ohne dass etwas anderes passiert"? Wenn es sich um eine öffentliche Methode handelt, handelt es sich um eine öffentliche Methode. Wenn Sie es verwenden, um einen Nebeneffekt zu erzielen, und es öffentlich ist, sollten Sie sich besser darauf verlassen können, dass alles so funktioniert, als ob nur der beabsichtigte Nebeneffekt aufgetreten wäre (mit allen anderen Implementierungsdetails und anderen Nebenwirkungen, Ressourcennutzung, was auch immer , versteckt vor den Bedenken des Benutzers). Dies ist bei Python absolut nicht anders. Die Syntax von Python, um den Effekt zu erzielen, ist einfach.
ely

25

Nun, ich möchte nur hinzufügen, dass wir, selbst wenn sie manchmal für die Kapselung und Sicherheit Ihrer Variablen / Objekte notwendig sind, wenn wir ein echtes objektorientiertes Programm codieren möchten , aufhören müssen, die Accessoires zu überbeanspruchen , weil wir manchmal sehr abhängig sind auf sie wann ist nicht wirklich notwendig und das macht fast das gleiche, als ob wir die Variablen öffentlich machen.


24

Danke, das hat mein Denken wirklich klargestellt. Hier sind (fast) 10 (fast) gute Gründe, Getter und Setter NICHT zu verwenden:

  1. Wenn Sie feststellen, dass Sie mehr tun müssen, als nur den Wert festzulegen und abzurufen, können Sie das Feld einfach privat machen, wodurch Sie sofort erfahren, wo Sie direkt darauf zugegriffen haben.
  2. Jede Validierung, die Sie dort durchführen, kann nur kontextfrei sein, was in der Praxis selten der Fall ist.
  3. Sie können den eingestellten Wert ändern - dies ist ein absoluter Albtraum, wenn der Anrufer Ihnen einen Wert übergibt, den Sie [Schockhorror] so wie er ist speichern sollen.
  4. Sie können die interne Darstellung ausblenden - fantastisch, also stellen Sie sicher, dass alle diese Operationen symmetrisch sind, oder?
  5. Sie haben Ihre öffentliche Benutzeroberfläche vor Änderungen unter den Arbeitsblättern isoliert. Wenn Sie eine Benutzeroberfläche entworfen haben und nicht sicher waren, ob der direkte Zugriff auf etwas in Ordnung ist, sollten Sie weiter entwerfen.
  6. Einige Bibliotheken erwarten dies, aber nicht viele - Reflexion, Serialisierung und Scheinobjekte funktionieren mit öffentlichen Feldern einwandfrei.
  7. Wenn Sie diese Klasse erben, können Sie die Standardfunktionalität überschreiben. Mit anderen Worten, Sie können Anrufer WIRKLICH verwirren, indem Sie die Implementierung nicht nur ausblenden, sondern auch inkonsistent machen.

Die letzten drei verlasse ich gerade (N / A oder D / C) ...


11
Ich denke, das entscheidende Argument ist: "Wenn Sie eine Schnittstelle entworfen haben und nicht sicher waren, ob der direkte Zugriff auf etwas in Ordnung ist, sollten Sie weiter entwerfen." Das ist das wichtigste Problem bei Gettern / Setzern: Sie reduzieren eine Klasse auf einen bloßen Container mit (mehr oder weniger) öffentlichen Feldern. In der realen OOP ist ein Objekt jedoch mehr als ein Container mit Datenfeldern. Es kapselt den Zustand und Algorithmen, um diesen Zustand zu manipulieren. Entscheidend an dieser Aussage ist, dass der Zustand gekapselt und nur durch die vom Objekt bereitgestellten Algorithmen manipuliert werden soll.
sbi

22

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

Ich habe einen kleinen Leistungstest gemacht. Ich habe eine Klasse "NumberHolder" geschrieben, die eine Ganzzahl enthält. Sie können diese Ganzzahl entweder mit der Getter-Methode lesen anInstance.getNumber()oder direkt mit auf die Zahl zugreifen anInstance.number. Mein Programm liest die Nummer 1.000.000.000 Mal auf beide Arten. Dieser Vorgang wird fünfmal wiederholt und die Zeit 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)

Sie sehen, der Getter ist (fast) immer ein bisschen schneller. Dann habe ich es mit unterschiedlich vielen Zyklen versucht. Anstelle von 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.000 (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.


3
Es gibt einen "spürbaren" Overhead mit einem Funktionsaufruf, um auf den Speicher zuzugreifen, anstatt einfach die Adresse eines Objekts zu laden und einen Offset hinzuzufügen, um auf die Mitglieder zuzugreifen. Die Chancen stehen gut, dass die VM Ihren Getter ohnehin flach optimiert. Unabhängig davon lohnt es sich nicht, den genannten Overhead zu verlieren, um alle Vorteile von Gettern / Setzern zu verlieren.
Alex

16

Verwenden Sie keine Getter-Setter, es sei denn, dies wird für Ihre aktuelle Lieferung benötigt. Denken Sie also nicht zu viel darüber nach, was in Zukunft passieren wird. Wenn etwas geändert werden muss, ist dies in den meisten Produktionsanwendungen und -systemen eine Änderungsanforderung.

Denken Sie einfach, leicht und fügen Sie bei Bedarf Komplexität hinzu.

Ich würde die Unwissenheit von Geschäftsinhabern mit tiefem technischen Know-how nicht ausnutzen, nur weil ich denke, dass es richtig ist oder ich den Ansatz mag.

Ich habe ein massives System ohne Getter-Setter geschrieben, nur mit Zugriffsmodifikatoren und einigen Methoden zur Validierung und Durchführung von Biz-Logik. Wenn Sie das unbedingt brauchten. Verwenden Sie alles.


16

Wir verwenden Getter und Setter:

  • zur Wiederverwendbarkeit
  • Validierung in späteren Phasen der Programmierung durchzuführen

Getter- und Setter-Methoden sind öffentliche Schnittstellen für den Zugriff auf private Klassenmitglieder.


Verkapselungsmantra

Das Kapselungsmantra besteht darin, Felder privat und Methoden öffentlich zu machen.

Getter-Methoden: Wir können auf private Variablen zugreifen.

Setter-Methoden: Wir können private Felder ändern.

Obwohl die Getter- und Setter-Methoden keine neuen Funktionen hinzufügen, können wir unsere Meinung später ändern, um diese Methode zu erstellen

  • besser;
  • sicherer; und
  • schneller.

Überall dort, wo ein Wert verwendet werden kann, kann eine Methode hinzugefügt werden, die diesen Wert zurückgibt. Anstatt:

int x = 1000 - 500

verwenden

int x = 1000 - class_name.getValue();

In Laienbegriffen

Darstellung der Klasse "Person"

Angenommen, wir müssen die Details dazu speichern Person. Dies Personhat die Felder name, ageund sex. Dabei beinhaltet Verfahren für die Erstellung name, ageund sex. Wenn wir nun eine andere Person erstellen müssen, müssen die Methoden für name,, erstellt agewerden.sex alle immer wieder.

Stattdessen können wir eine Bean class(Person)mit Getter- und Setter-Methoden erstellen . Morgen können wir also einfach Objekte dieser Bean erstellen, class(Person class)wenn wir eine neue Person hinzufügen müssen (siehe Abbildung). Daher verwenden wir die Felder und Methoden der Bean-Klasse wieder, was viel besser ist.


15

Ich habe eine ganze Weile darüber nachgedacht, was den Java-Fall betrifft, und ich glaube, die wahren Gründe sind:

  1. Code für die Schnittstelle, nicht für die Implementierung
  2. Schnittstellen geben nur Methoden an, keine Felder

Mit anderen Worten, Sie können ein Feld in einer Schnittstelle nur angeben, indem Sie eine Methode zum Schreiben eines neuen Werts und eine Methode zum Lesen des aktuellen Werts bereitstellen.

Diese Methoden sind der berüchtigte Getter und Setter ....


1
Okay, zweite Frage; Für den Fall, dass es sich um ein Projekt handelt, bei dem Sie keine Quelle an Dritte exportieren und die volle Kontrolle über die Quelle haben. Gewinnen Sie mit Gettern und Setzern etwas?
Dean J

2
In jedem nicht trivialen Java-Projekt müssen Sie Schnittstellen codieren, um die Dinge handhabbar und testbar zu machen (denken Sie an Modelle und Proxy-Objekte). Wenn Sie Schnittstellen verwenden, benötigen Sie Getter und Setter.
Thorbjørn Ravn Andersen

15

Es kann zum verzögerten Laden nützlich sein. Angenommen, das betreffende Objekt ist in einer Datenbank gespeichert, und Sie möchten es nur abrufen, wenn Sie es benötigen. Wenn das Objekt von einem Getter abgerufen wird, kann das interne Objekt null sein, bis jemand danach fragt. Dann können Sie es beim ersten Aufruf des Getters abrufen.

Ich hatte eine Basisseitenklasse in einem Projekt, die mir übergeben wurde und die einige Daten aus verschiedenen Webdienstaufrufen lud, aber die Daten in diesen Webdienstaufrufen wurden nicht immer auf allen untergeordneten Seiten verwendet. Webdienste bieten trotz aller Vorteile Pionierarbeit für neue Definitionen von "langsam". Sie möchten also keinen Webdienstaufruf tätigen, wenn Sie dies nicht müssen.

Ich bin von öffentlichen Feldern zu Getter gewechselt, und jetzt überprüfen die Getter den Cache. Wenn er nicht vorhanden ist, rufen Sie den Webdienst an. Mit ein wenig Umbruch wurden viele Web-Service-Anrufe verhindert.

Der Getter erspart mir also den Versuch, auf jeder untergeordneten Seite herauszufinden, was ich brauche. Wenn ich es brauche, rufe ich den Getter an und es wird für mich gefunden, wenn ich es noch nicht habe.

    protected YourType _yourName = null;
    public YourType YourName{
      get
      {
        if (_yourName == null)
        {
          _yourName = new YourType();
          return _yourName;
        }
      }
    }

Also ruft der Getter den Setter an?
icc97

Ich habe ein Codebeispiel hinzugefügt, wie ich es in der Vergangenheit gemacht habe. Im Wesentlichen speichern Sie die tatsächliche Klasse in einem geschützten Mitglied und geben dieses geschützte Mitglied im get-Accessor zurück und initialisieren es, wenn es nicht initialisiert ist.
Federbrecher


13

Ein Aspekt, den ich bisher in den Antworten vermisst habe, ist die Zugangsspezifikation:

  • Für Mitglieder haben Sie nur eine Zugriffsspezifikation zum Einstellen und Abrufen
  • Für Setter und Getter können Sie es fein abstimmen und separat definieren

13

EDIT: Ich habe diese Frage beantwortet, weil es eine Menge Leute gibt, die das Programmieren lernen, und die meisten Antworten sind sehr technisch kompetent, aber sie sind nicht so einfach zu verstehen, wenn Sie ein Neuling sind. Wir waren alle Neulinge, also dachte ich, ich würde mich an einer neulingsfreundlicheren Antwort versuchen.

Die beiden wichtigsten sind Polymorphismus und Validierung. Auch wenn es nur eine blöde Datenstruktur ist.

Nehmen wir an, wir haben diese einfache Klasse:

public class Bottle {
  public int amountOfWaterMl;
  public int capacityMl;
}

Eine sehr einfache Klasse, die angibt, wie viel Flüssigkeit sich darin befindet und wie hoch ihre Kapazität ist (in Millilitern).

Was passiert, wenn ich:

Bottle bot = new Bottle();
bot.amountOfWaterMl = 1500;
bot.capacityMl = 1000;

Nun, Sie würden nicht erwarten, dass das funktioniert, oder? Sie möchten, dass es eine Art Überprüfung der geistigen Gesundheit gibt. Und schlimmer noch, was ist, wenn ich nie die maximale Kapazität angegeben habe? Oh je, wir haben ein Problem.

Aber es gibt noch ein anderes Problem. Was wäre, wenn Flaschen nur eine Art von Behälter wären? Was wäre, wenn wir mehrere Behälter hätten, alle mit Kapazitäten und Flüssigkeitsmengen gefüllt? Wenn wir nur eine Schnittstelle erstellen könnten, könnten wir den Rest unseres Programms diese Schnittstelle akzeptieren lassen, und Flaschen, Kanister und alle möglichen Dinge würden einfach austauschbar funktionieren. Wäre das nicht besser? Da Schnittstellen Methoden erfordern, ist dies auch eine gute Sache.

Wir würden am Ende so etwas wie:

public interface LiquidContainer {
  public int getAmountMl();
  public void setAmountMl(int amountMl);
  public int getCapacityMl();
}

Großartig! Und jetzt ändern wir einfach die Flasche in diese:

public class Bottle extends LiquidContainer {
  private int capacityMl;
  private int amountFilledMl;

  public Bottle(int capacityMl, int amountFilledMl) {
    this.capacityMl = capacityMl;
    this.amountFilledMl = amountFilledMl;
    checkNotOverFlow();
  }

  public int getAmountMl() {
    return amountFilledMl;
  }

  public void setAmountMl(int amountMl) {
     this.amountFilled = amountMl;
     checkNotOverFlow();
  }
  public int getCapacityMl() {
    return capacityMl;
  }

  private void checkNotOverFlow() {
    if(amountOfWaterMl > capacityMl) {
      throw new BottleOverflowException();
    }
}

Ich überlasse die Definition der BottleOverflowException als Übung dem Leser.

Beachten Sie nun, wie viel robuster dies ist. Wir können jetzt mit jeder Art von Container in unserem Code umgehen, indem wir LiquidContainer anstelle von Bottle akzeptieren. Und wie diese Flaschen mit solchen Dingen umgehen, kann unterschiedlich sein. Sie können Flaschen haben, die ihren Status auf die Festplatte schreiben, wenn sie sich ändern, oder Flaschen, die in SQL-Datenbanken oder GNU speichern, wissen was noch.

Und all dies kann verschiedene Möglichkeiten haben, mit verschiedenen Whoopsies umzugehen. Die Flasche prüft nur, ob sie überläuft, und löst eine RuntimeException aus. Aber das könnte falsch sein. (Es gibt eine nützliche Diskussion über die Fehlerbehandlung, aber ich halte sie hier absichtlich sehr einfach. Leute in Kommentaren werden wahrscheinlich auf die Mängel dieses simplen Ansatzes hinweisen .;))

Und ja, es scheint, als würden wir von einer sehr einfachen Idee schnell zu viel besseren Antworten gelangen.

Bitte beachten Sie auch, dass Sie die Kapazität einer Flasche nicht ändern können. Es ist jetzt in Stein gemeißelt. Sie können dies mit einem int tun, indem Sie es für endgültig erklären. Aber wenn dies eine Liste wäre, könnten Sie sie leeren, neue Dinge hinzufügen und so weiter. Sie können den Zugriff auf das Berühren der Innereien nicht einschränken.

Es gibt auch die dritte Sache, die nicht jeder angesprochen hat: Getter und Setter verwenden Methodenaufrufe. Das bedeutet, dass sie überall wie normale Methoden aussehen. Anstatt eine seltsame spezifische Syntax für DTOs und andere Dinge zu haben, haben Sie überall das Gleiche.


2
Vielen Dank für die erste halbwegs anständige Erklärung der Schnittstellen, die ich gelesen habe, ohne Verweise auf "Fernbedienungen" oder "Autos"
DJVs

1
"Sie können Flaschen haben, die ihren Status auf die Festplatte schreiben, wenn sie sich ändern, oder Flaschen, die in SQL-Datenbanken speichern", räkelte ich mich so hart xD. Aber trotzdem eine schöne Idee, es zu präsentieren!
Xerus

10

In Sprachen, die keine "Eigenschaften" unterstützen (C ++, Java) oder eine Neukompilierung von Clients erfordern, wenn Felder in Eigenschaften (C #) geändert werden, ist die Verwendung von get / set-Methoden einfacher zu ändern. Zum Hinzufügen einer Validierungslogik zu einer setFoo-Methode muss beispielsweise die öffentliche Schnittstelle einer Klasse nicht geändert werden.

In Sprachen, die "echte" Eigenschaften unterstützen (Python, Ruby, vielleicht Smalltalk?), Ist es sinnlos, Methoden abzurufen / festzulegen.


1
Betreff: C #. Wenn Sie einem get / set Funktionen hinzufügen, muss das dann nicht trotzdem neu kompiliert werden?
Dampfer25

@ steamer25: Entschuldigung, falsch eingegeben. Ich meinte, dass Kunden der Klasse neu kompiliert werden müssen.
John Millikin

5
Das Hinzufügen einer Validierungslogik zu einer setFoo-Methode erfordert keine Änderung der Schnittstelle einer Klasse auf Sprachebene , ändert jedoch die tatsächliche Schnittstelle , auch als Vertrag bezeichnet, da dadurch die Voraussetzungen geändert werden. Warum sollte man wollen, dass der Compiler das nicht als bahnbrechende Änderung behandelt, wenn es so ist ?
R. Martinho Fernandes

@ R.MartinhoFernandes wie behebt man dieses "kaputte" Problem? Der Compiler kann nicht erkennen, ob er kaputt geht oder nicht. Dies ist nur ein Problem, wenn Sie Bibliotheken für andere schreiben, aber Sie machen es als universelles zOMG hier, seien Sie Drachen!
Phil

3
Das Erfordernis einer Neukompilierung, wie in der Antwort erwähnt, ist eine Möglichkeit, mit der der Compiler Sie auf eine mögliche Änderung aufmerksam machen kann. Und fast alles, was ich schreibe, ist effektiv "eine Bibliothek für andere", weil ich nicht alleine arbeite. Ich schreibe Code mit Schnittstellen, die andere Personen im Projekt verwenden werden. Was ist der Unterschied? Zur Hölle, selbst wenn ich der Benutzer dieser Schnittstellen sein werde, warum sollte ich meinen Code auf niedrigeren Qualitätsstandards halten? Ich mag es nicht, mit störenden Schnittstellen zu arbeiten, auch wenn ich derjenige bin, der sie schreibt.
R. Martinho Fernandes

6

Eines der Grundprinzipien des OO-Designs: Verkapselung!

Es bietet Ihnen viele Vorteile. Einer davon ist, dass Sie die Implementierung des Getters / Setters hinter den Kulissen ändern können, aber jeder Verbraucher mit diesem Wert arbeitet weiter, solange der Datentyp gleich bleibt.


23
Das Angebot an Verkapselungs-Gettern und -Setzern ist lächerlich dünn. Siehe hier .
sbi

Wenn Ihre öffentliche Schnittstelle angibt, dass 'foo' vom Typ 'T' ist und auf irgendetwas gesetzt werden kann, können Sie dies niemals ändern. Sie können sich weder für den Typ 'Y' entscheiden, noch Regeln wie Größenbeschränkungen festlegen. Wenn Sie also ein öffentliches Get / Set haben, das nicht viel Set / Get macht, gewinnen Sie nichts, was ein öffentliches Feld nicht bietet, und machen die Verwendung umständlicher. Wenn Sie eine Einschränkung haben, z. B. dass das Objekt auf null gesetzt werden kann oder der Wert innerhalb eines Bereichs liegen muss, ist eine öffentliche Set-Methode erforderlich, aber Sie legen immer noch einen Vertrag vor, um diesen Wert festzulegen, der ' t change
thecoshman

Warum kann ein Vertrag nicht geändert werden?
Phil

5
Warum sollten die Dinge weiter kompiliert werden, wenn sich Verträge ändern?
R. Martinho Fernandes

5

Sie sollten Getter und Setter verwenden, wenn:

  • Sie haben es mit etwas zu tun, das konzeptionell ein Attribut ist, aber:
    • Ihre Sprache hat keine Eigenschaften (oder einen ähnlichen Mechanismus wie die variablen Spuren von Tcl) oder
    • Die Eigenschaftsunterstützung Ihrer Sprache reicht für diesen Anwendungsfall nicht aus
    • Die idiomatischen Konventionen Ihrer Sprache (oder manchmal Ihres Frameworks) ermutigen Getter oder Setter für diesen Anwendungsfall.

Dies ist also sehr selten eine allgemeine OO-Frage. Es ist eine sprachspezifische Frage mit unterschiedlichen Antworten für verschiedene Sprachen (und verschiedene Anwendungsfälle).


Aus Sicht der OO-Theorie sind Getter und Setter nutzlos. Die Schnittstelle Ihrer Klasse ist das, was sie tut, nicht der Zustand. (Wenn nicht, haben Sie die falsche Klasse geschrieben.) In sehr einfachen Fällen, in denen eine Klasse nur einen Punkt in rechteckigen Koordinaten darstellt, * sind die Attribute Teil der Schnittstelle. Getter und Setter trüben das einfach. In alles anderen als sehr einfachen Fällen sind jedoch weder die Attribute noch Getter und Setter Teil der Schnittstelle.

Anders ausgedrückt: Wenn Sie der Meinung sind, dass Verbraucher Ihrer Klasse nicht einmal wissen sollten, dass Sie ein spamAttribut haben, und noch weniger in der Lage sind, es wohl oder übel zu ändern, set_spamist es das Letzte, was Sie tun möchten , ihnen eine Methode zu geben.

* Selbst für diese einfache Klasse möchten Sie möglicherweise nicht unbedingt das Festlegen der Werte xund zulassen y. Wenn dies wirklich eine Klasse ist, sollte es nicht haben Methoden wie translate, rotateusw.? Wenn es nur eine Klasse ist, weil Ihre Sprache keine Datensätze / Strukturen / benannten Tupel hat, dann ist dies nicht wirklich eine Frage von OO…


Aber niemand macht jemals allgemeines OO-Design. Sie entwerfen und implementieren in einer bestimmten Sprache. Und in einigen Sprachen sind Getter und Setter alles andere als nutzlos.

Wenn Ihre Sprache keine Eigenschaften hat, können Sie etwas, das konzeptionell ein Attribut ist, aber tatsächlich berechnet oder validiert wird, nur über Getter und Setter darstellen.

Selbst wenn Ihre Sprache Eigenschaften hat, kann es Fälle geben, in denen sie unzureichend oder unangemessen sind. Wenn Sie beispielsweise zulassen möchten, dass Unterklassen die Semantik eines Attributs in Sprachen ohne dynamischen Zugriff steuern, kann eine Unterklasse ein Attribut nicht durch eine berechnete Eigenschaft ersetzen.

Was ist mit "Was ist, wenn ich meine Implementierung später ändern möchte?" Frage (die sowohl in der Frage des OP als auch in der akzeptierten Antwort mehrmals in unterschiedlichen Formulierungen wiederholt wird): Wenn es sich wirklich um eine reine Implementierungsänderung handelt und Sie mit einem Attribut begonnen haben, können Sie es in eine Eigenschaft ändern, ohne die Schnittstelle zu beeinflussen. Es sei denn natürlich, Ihre Sprache unterstützt das nicht. Das ist also wirklich wieder genau der gleiche Fall.

Es ist auch wichtig, den Redewendungen der Sprache (oder des Frameworks) zu folgen, die Sie verwenden. Wenn Sie schönen Code im Ruby-Stil in C # schreiben, wird jeder erfahrene C # -Entwickler außer Ihnen Probleme haben, ihn zu lesen, und das ist schlecht. Einige Sprachen haben um ihre Konventionen herum stärkere Kulturen als andere. - und es ist kein Zufall, dass Java und Python, die sich aufgrund ihrer Redewendungen am anderen Ende des Spektrums befinden, zufällig zwei der stärksten Kulturen haben.

Über menschliche Leser hinaus wird es Bibliotheken und Tools geben, die von Ihnen erwarten, dass Sie die Konventionen befolgen und Ihr Leben schwerer machen, wenn Sie dies nicht tun. Das Verknüpfen von Widgets des Interface Builder mit anderen Objekten als ObjC oder das Verwenden bestimmter Java-Verspottungsbibliotheken ohne Getter erschwert Ihr Leben nur. Wenn die Werkzeuge für Sie wichtig sind, bekämpfen Sie sie nicht.


4

Unter dem Gesichtspunkt der Objektorientierung können beide Alternativen die Wartung des Codes beeinträchtigen, indem sie die Kapselung der Klassen schwächen. Eine Diskussion finden Sie in diesem ausgezeichneten Artikel: http://typicalprogrammer.com/?p=23


3

Getter- und Setter-Methoden sind Accessor-Methoden, dh sie sind im Allgemeinen eine öffentliche Schnittstelle zum Ändern privater Klassenmitglieder. Sie verwenden Getter- und Setter-Methoden, um eine Eigenschaft zu definieren. Sie greifen auf Getter- und Setter-Methoden als Eigenschaften außerhalb der Klasse zu, obwohl Sie sie innerhalb der Klasse als Methoden definieren. Diese Eigenschaften außerhalb der Klasse können einen anderen Namen als der Eigenschaftsname in der Klasse haben.

Die Verwendung von Getter- und Setter-Methoden bietet einige Vorteile, z. B. die Möglichkeit, Mitglieder mit ausgefeilten Funktionen zu erstellen, auf die Sie wie ähnliche Eigenschaften zugreifen können. Sie können damit auch schreibgeschützte und schreibgeschützte Eigenschaften erstellen.

Obwohl Getter- und Setter-Methoden nützlich sind, sollten Sie darauf achten, sie nicht zu stark zu verwenden, da sie unter anderem in bestimmten Situationen die Codewartung erschweren können. Außerdem bieten sie wie öffentliche Mitglieder Zugriff auf Ihre Klassenimplementierung. Die OOP-Praxis rät vom direkten Zugriff auf Eigenschaften innerhalb einer Klasse ab.

Wenn Sie Klassen schreiben, werden Sie immer aufgefordert, so viele Instanzvariablen wie möglich privat zu machen und Getter- und Setter-Methoden entsprechend hinzuzufügen. Dies liegt daran, dass Sie möglicherweise nicht möchten, dass Benutzer bestimmte Variablen in Ihren Klassen ändern. Wenn Sie beispielsweise eine private statische Methode haben, die die Anzahl der für eine bestimmte Klasse erstellten Instanzen verfolgt, möchten Sie nicht, dass ein Benutzer diesen Zähler mithilfe von Code ändert. Nur die Konstruktoranweisung sollte diese Variable bei jedem Aufruf erhöhen. In dieser Situation können Sie eine private Instanzvariable erstellen und eine Getter-Methode nur für die Zählervariable zulassen. Dies bedeutet, dass Benutzer den aktuellen Wert nur mithilfe der Getter-Methode abrufen können und keine neuen Werte festlegen können mit der Setter-Methode.


3

Code entwickelt sich . privateist ideal, wenn Sie den Schutz von Datenmitgliedern benötigen . Schließlich sollten alle Klassen eine Art "Miniprogramm" sein, das eine genau definierte Schnittstelle hat , die Sie nicht einfach mit den Interna von verschrauben können .

Bei der Softwareentwicklung geht es jedoch nicht darum, die endgültige Version der Klasse so festzulegen, als würden Sie beim ersten Versuch eine gusseiserne Statue drücken. Während Sie damit arbeiten, ähnelt Code eher Ton. Es entwickelt sich, während Sie es entwickeln und mehr über die Problemdomäne erfahren, die Sie lösen. Während der Entwicklung können Klassen miteinander interagieren, als sie sollten (Abhängigkeit, die Sie herausrechnen möchten), zusammenführen oder aufteilen. Ich denke, die Debatte läuft darauf hinaus, dass Menschen nicht religiös schreiben wollen

int getVar() const { return var ; }

Also hast du:

doSomething( obj->getVar() ) ;

Anstatt

doSomething( obj->var ) ;

Es ist nicht nur getVar()visuell verrauscht, es gibt auch diese Illusion, gettingVar()die irgendwie komplexer ist als es wirklich ist. Wie Sie (als Klassenschreiber) die Heiligkeit von betrachten, varist für einen Benutzer Ihrer Klasse besonders verwirrend, wenn er einen Durchgangssetzer hat - dann sieht es so aus, als würden Sie diese Tore aufstellen, um etwas zu "schützen", auf dem Sie bestehen, ist wertvoll. (die Heiligkeit von var), aber selbst Sie geben zu, dass varder Schutz nicht viel wert ist, weil jemand einfach hereinkommen kann undset var zu jedem Wert, den er will, ohne dass Sie auch nur einen Blick auf das werfen, was er tut.

Ich programmiere also wie folgt (unter der Annahme eines "agilen" Ansatzes - dh wenn ich Code schreibe, der nicht genau weiß was er tun wird / keine Zeit oder Erfahrung hat, um ein ausgeklügeltes Interface-Set im Wasserfall-Stil zu planen):

1) Beginnen Sie mit allen öffentlichen Mitgliedern für grundlegende Objekte mit Daten und Verhalten. Aus diesem Grund werden Sie in meinem gesamten C ++ - Beispielcode feststellen, dass ich ihn structnicht classüberall verwende.

2) Wenn das interne Verhalten eines Objekts für ein Datenelement komplex genug wird (z. B. ein internes std::listin einer bestimmten Reihenfolge beibehalten möchte), werden Funktionen vom Typ Accessor geschrieben. Da ich selbst programmiere, setze ich das Mitglied nicht immer privatesofort, aber irgendwo in der Entwicklung der Klasse wird das Mitglied entweder protectedoder befördertprivate .

3) Klassen, die vollständig ausgearbeitet sind und strenge Regeln für ihre Interna haben (dh sie wissen genau, was sie tun, und Sie dürfen nicht mit ihren Interna "ficken" (Fachbegriff)), erhalten die classBezeichnung "Standard-Privatmitglieder". und nur einige wenige Mitglieder dürfen sein public.

Ich finde, dass dieser Ansatz es mir ermöglicht, nicht dort zu sitzen und religiös Getter / Setter zu schreiben, wenn viele Datenmitglieder in den frühen Stadien der Entwicklung einer Klasse migriert, verschoben usw. werden.


1
"... eine gut definierte Schnittstelle, die man nicht einfach mit den Interna von" und der Validierung in Setzern verschrauben kann.
Agi Hammerthief

3

Es gibt einen guten Grund, die Verwendung von Accessoren in Betracht zu ziehen, da keine Eigenschaftsvererbung vorliegt. Siehe nächstes Beispiel:

public class TestPropertyOverride {
    public static class A {
        public int i = 0;

        public void add() {
            i++;
        }

        public int getI() {
            return i;
        }
    }

    public static class B extends A {
        public int i = 2;

        @Override
        public void add() {
            i = i + 2;
        }

        @Override
        public int getI() {
            return i;
        }
    }

    public static void main(String[] args) {
        A a = new B();
        System.out.println(a.i);
        a.add();
        System.out.println(a.i);
        System.out.println(a.getI());
    }
}

Ausgabe:

0
0
4

3

Getters und Setter werden verwendet, um zwei der grundlegenden Aspekte der objektorientierten Programmierung zu implementieren:

  1. Abstraktion
  2. Verkapselung

Angenommen, wir haben eine Mitarbeiterklasse:

package com.highmark.productConfig.types;

public class Employee {

    private String firstName;
    private String middleName;
    private String lastName;

    public String getFirstName() {
      return firstName;
    }
    public void setFirstName(String firstName) {
       this.firstName = firstName;
    }
    public String getMiddleName() {
        return middleName;
    }
    public void setMiddleName(String middleName) {
         this.middleName = middleName;
    }
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getFullName(){
        return this.getFirstName() + this.getMiddleName() +  this.getLastName();
    }
 }

Hier sind die Implementierungsdetails von Full Name für den Benutzer verborgen und für den Benutzer im Gegensatz zu einem öffentlichen Attribut nicht direkt zugänglich.


1
Für mich ist es nutzlos, Tonnen von Gettern und Setzern zu haben, die nichts Einzigartiges tun. getFullName ist eine Ausnahme, weil es etwas anderes tut. Wenn nur die drei Variablen öffentlich sind und dann getFullName beibehalten wird, ist das Programm leichter zu lesen, aber der vollständige Name bleibt verborgen. Im Allgemeinen bin ich völlig in Ordnung mit Gettern und Setzern, wenn a. sie machen etwas Einzigartiges und / oder b. Sie haben nur eine, ja, Sie könnten ein öffentliches Finale haben und das alles außer nah
FacelessTiger

1
Dies hat den Vorteil, dass Sie die Interna der Klasse ändern können, ohne die Schnittstelle zu ändern. Angenommen, Sie hatten anstelle von drei Eigenschaften eine - eine Reihe von Zeichenfolgen. Wenn Sie Getter und Setter verwendet haben, können Sie diese Änderung vornehmen und dann die Getter / Setter aktualisieren, um zu wissen, dass Namen [0] Vorname, Namen [1] Mitte usw. sind. Wenn Sie jedoch nur öffentliche Eigenschaften verwendet haben Außerdem müssten Sie jede Klasse ändern, auf die Employee zugegriffen wird, da die von ihnen verwendete Eigenschaft firstName nicht mehr vorhanden ist.
Andrew Hows

1
@AndrewHows von dem, was ich im wirklichen Leben gesehen habe, wenn Leute die Interna der Klasse ändern, ändern sie auch die Schnittstelle und führen eine große
Umgestaltung

2

Eine andere Verwendung (in Sprachen, die Eigenschaften unterstützen) besteht darin, dass Setter und Getter implizieren können, dass eine Operation nicht trivial ist. In der Regel möchten Sie vermeiden, etwas zu tun, das in einer Immobilie rechenintensiv ist.


Ich würde niemals erwarten, dass ein Getter oder Setter eine teure Operation ist. In solchen Fällen besser , eine Fabrik verwenden: alle Setter verwenden Sie brauchen , und dann rufen Sie das teuer executeoder buildVerfahren.
Hubert Grzeskowiak

2

Ein relativ moderner Vorteil von Gettern / Setzern besteht darin, dass das Durchsuchen von Code in markierten (indizierten) Code-Editoren vereinfacht wird. Beispiel: Wenn Sie sehen möchten, wer ein Mitglied setzt, können Sie die Anrufhierarchie des Setters öffnen.

Wenn das Mitglied öffentlich ist, können die Tools den Lese- / Schreibzugriff auf das Mitglied nicht filtern. Sie müssen also durch alle Verwendungszwecke des Mitglieds stapfen.


Sie können richtig klicken, um die Verwendung für ein Mitglied genau wie für einen Getter / Setter zu finden
Eildosa

2

In einer objektorientierten Sprache deklarieren die Methoden und ihre Zugriffsmodifikatoren die Schnittstelle für dieses Objekt. Zwischen dem Konstruktor und den Accessor- und Mutator-Methoden kann der Entwickler den Zugriff auf den internen Status eines Objekts steuern. Wenn die Variablen einfach als öffentlich deklariert werden, gibt es keine Möglichkeit, diesen Zugriff zu regulieren. Und wenn wir Setter verwenden, können wir den Benutzer für die Eingabe einschränken, die wir benötigen. Das heißt, der Feed für diese Variable wird über einen geeigneten Kanal gesendet und der Kanal wird von uns vordefiniert. Es ist also sicherer, Setter zu verwenden.


1

Darüber hinaus dient dies dazu, Ihre Klasse "zukunftssicher" zu machen. Insbesondere der Wechsel von einem Feld zu einer Eigenschaft ist eine ABI-Unterbrechung. Wenn Sie also später entscheiden, dass Sie mehr Logik als nur "Setzen / Abrufen des Felds" benötigen, müssen Sie ABI unterbrechen, was natürlich Probleme für alles verursacht sonst schon gegen deine Klasse kompiliert.


2
Ich nehme an, dass das Ändern des Verhaltens des Getters oder Setters dann keine bahnbrechende Änderung ist. </ sarcasm>
R. Martinho Fernandes

@ R.MartinhoFernandes muss nicht immer sein. TBYS
Phil

1

Ich möchte nur die Idee der Annotation werfen: @getter und @setter. Mit @getter sollten Sie in der Lage sein, obj = class.field, aber nicht class.field = obj. Mit @setter umgekehrt. Mit @getter und @setter sollten Sie in der Lage sein, beides zu tun. Dies würde die Kapselung bewahren und die Zeit verkürzen, indem zur Laufzeit keine trivialen Methoden aufgerufen werden.


1
Es würde zur Laufzeit mit "trivialen Methoden" implementiert. Eigentlich wahrscheinlich nicht trivial.
Phil

Heute kann dies mit einem Annotation-Präprozessor durchgeführt werden.
Thorbjørn Ravn Andersen
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.