Wann sind Getter und Setter gerechtfertigt?

175

Getter und Setter werden oft als nicht richtig kritisiert. Andererseits hat der meiste OO-Code, den ich gesehen habe, umfangreiche Getter und Setter.

Wann sind Getter und Setter gerechtfertigt? Versuchen Sie, sie zu vermeiden? Sind sie im Allgemeinen überbeansprucht?

Wenn Ihre Lieblingssprache Eigenschaften hat (meine hat), werden solche Dinge auch als Stärkungsmittel für diese Frage angesehen. Aus Sicht der OO-Methodik sind sie dasselbe. Sie haben nur eine schönere Syntax.

Quellen für Kritik an Gettern / Setzern (einige stammen aus Kommentaren, um die Sichtbarkeit zu verbessern):

Um die Kritik einfach zu formulieren: Mit Getters und Setters können Sie den internen Zustand von Objekten von außerhalb des Objekts manipulieren. Dies verletzt die Kapselung. Nur das Objekt selbst sollte sich um seinen internen Zustand kümmern.

Und eine Beispielprozedurversion des Codes.

struct Fridge
{
    int cheese;
}

void go_shopping(Fridge fridge)
{
     fridge.cheese += 5;
}

Mutator-Version des Codes:

class Fridge
{
     int cheese;

     void set_cheese(int _cheese) { cheese = _cheese; }
     int get_cheese() { return cheese; }
 }

void go_shopping(Fridge fridge)
{
     fridge.set_cheese(fridge.get_cheese() + 5);        
}

Die Getter und Setter machten den Code viel komplizierter, ohne dass eine ordnungsgemäße Kapselung möglich war. Da der interne Zustand für andere Objekte zugänglich ist, gewinnen wir durch Hinzufügen dieser Getter und Setter nicht viel.

Die Frage wurde bereits zu Stack Overflow erörtert:

Winston Ewert
quelle
21
Getters and setters are often criticized as being not proper OO- Bitte zitieren.
Robert Harvey
45
@Job, warum denn es hat Code? Es ist eine gute Frage und stinkt nach heiligem Krieg, wenn man es ernst nimmt.
Peter Turner
2
@ Winston Ewert: "... die Frage ist, ob Sie überhaupt auf die Daten innerhalb der Klasse zugreifen sollten.": Nun, niemand zwingt Sie dazu, Get- und Setter für alle Mitgliedsvariablen zu implementieren, Sie implementieren nur die, die Sie benötigen. Aus diesem Grund verwenden Sie Get- und Setter-Variablen anstelle von öffentlichen Member-Variablen.
Giorgio
2
@ Winston Ewert: Ich verstehe nicht, wie das Fehlen von Gettern / Setzern dieses Problem lösen würde: Es muss einen Weg geben, auf jedes Datenelement zuzugreifen, sonst ist es nutzlos. Es ist die Aufgabe eines guten Designs, zu entscheiden, welcher Teil der Daten für welchen Teil des Codes zugänglich ist. Wenn jemand ein schlechter Designer ist, wird er oder sie es mit oder ohne Getter und Setter sein. Nur meine 2 Cent.
Giorgio

Antworten:

162

Getter und Setter zu haben, bricht nicht die Kapselung. Bei break encapsulation wird automatisch ein Getter und ein Setter für jedes Datenelement (jedes Feld in Java-Jargon) hinzugefügt , ohne dass darüber nachgedacht wird. Dies ist zwar besser als die Veröffentlichung aller Datenelemente, aber nur einen kleinen Schritt entfernt.

Der Punkt der Kapselung ist nicht, dass Sie den Zustand des Objekts nicht von außerhalb des Objekts kennen oder ändern können sollten, sondern dass Sie eine vernünftige Richtlinie dafür haben sollten.

  • Einige Datenelemente befinden sich möglicherweise vollständig im Objekt und sollten weder Getter noch Setter enthalten.

  • Einige Datenelemente sollten schreibgeschützt sein, daher benötigen sie möglicherweise Getter, aber keine Setter.

  • Einige Datenelemente müssen möglicherweise konsistent gehalten werden. In einem solchen Fall würden Sie nicht für jeden einen Setter bereitstellen, sondern eine einzige Methode, um sie gleichzeitig festzulegen, damit Sie die Werte auf Konsistenz prüfen können.

  • Einige Datenelemente müssen möglicherweise nur auf eine bestimmte Weise geändert werden, z. B. um einen festen Betrag inkrementiert oder dekrementiert. In diesem Fall würden Sie eine increment()und / oder decrement()Methode anstelle eines Setters angeben.

  • Wieder andere müssen möglicherweise tatsächlich Lese- und Schreibzugriffe ausführen und haben sowohl einen Getter als auch einen Setter.

Betrachten Sie ein Beispiel für eine class Person. Angenommen, eine Person hat einen Namen, eine Sozialversicherungsnummer und ein Alter. Nehmen wir an, wir erlauben Menschen nicht, jemals ihren Namen oder ihre Sozialversicherungsnummer zu ändern. Das Alter der Person sollte jedoch jedes Jahr um 1 erhöht werden. In diesem Fall würden Sie einen Konstruktor bereitstellen, der den Namen und die SSN mit den angegebenen Werten initialisiert und das Alter auf 0 initialisiert. Sie würden auch eine Methode bereitstellen incrementAge(), die das Alter um 1 erhöht Getter für alle drei. In diesem Fall sind keine Einsteller erforderlich.

In diesem Entwurf können Sie zulassen, dass der Status des Objekts von außerhalb der Klasse überprüft wird, und Sie können zulassen, dass er von außerhalb der Klasse geändert wird. Sie können jedoch nicht zulassen, dass der Status willkürlich geändert wird. Es gibt eine Richtlinie, die effektiv festlegt, dass der Name und die SSN überhaupt nicht geändert werden können und dass das Alter jeweils um ein Jahr erhöht werden kann.

Nehmen wir an, eine Person hat auch ein Gehalt. Und die Leute können Jobs nach Belieben wechseln, was bedeutet, dass sich auch ihr Gehalt ändert. Um diese Situation zu modellieren, haben wir keine andere Möglichkeit, als eine setSalary()Methode bereitzustellen ! In diesem Fall ist es durchaus angemessen, das Gehalt nach Belieben ändern zu lassen.

By the way, in Ihrem Beispiel, würde ich die Klasse gibt Fridgedie putCheese()und takeCheese()Methoden, statt get_cheese()und set_cheese(). Dann hättest du noch Kapselung.


public class Fridge {
  private List objects;
  private Date warranty;

  /** How the warranty is stored internally is a detail. */
  public Fridge( Date warranty ) {
    // The Fridge can set its internal warranty, but it is not re-exposed.
    setWarranty( warranty );
  }

  /** Doesn't expose how the fridge knows it is empty. */
  public boolean isEmpty() {
    return getObjects().isEmpty();
  }

  /** When the fridge has no more room... */
  public boolean isFull() {
  }

  /** Answers whether the given object will fit. */
  public boolean canStore( Object o ) {
    boolean result = false;

    // Clients may not ask how much room remains in the fridge.
    if( o instanceof PhysicalObject ) {
      PhysicalObject po = (PhysicalObject)o;

      // How the fridge determines its remaining usable volume is a detail.
      // How a physical object determines whether it fits within a specified
      // volume is also a detail.
      result = po.isEnclosedBy( getUsableVolume() );
    }

     return result;
  }

  /** Doesn't expose how the fridge knows its warranty has expired. */
  public boolean isPastWarranty() {
    return getWarranty().before( new Date() );
  }

  /** Doesn't expose how objects are stored in the fridge. */
  public synchronized void store( Object o ) {
    validateExpiration( o );

    // Can the object fit?
    if( canStore( o ) ) {
      getObjects().add( o );
    }
    else {
      throw FridgeFullException( o );
    }
  }

  /** Doesn't expose how objects are removed from the fridge. */
  public synchronized void remove( Object o ) {
    if( !getObjects().contains( o ) ) {
      throw new ObjectNotFoundException( o );
    }

    getObjects().remove( o );

    validateExpiration( o );
  }

  /** Lazily initialized list, an implementation detail. */
  private synchronized List getObjects() {
    if( this.list == null ) { this.list = new List(); }
    return this.list;
  }

  /** How object expiration is determined is also a detail. */
  private void validateExpiration( Object o ) {
    // Objects can answer whether they have gone past a given
    // expiration date. How each object "knows" it has expired
    // is a detail. The Fridge might use a scanner and
    // items might have embedded RFID chips. It's a detail hidden
    // by proper encapsulation.
    if( o implements Expires && ((Expires)o).expiresBefore( today ) ) {
      throw new ExpiredObjectException( o );
    }
  }

  /** This creates a copy of the warranty for immutability purposes. */
  private void setWarranty( Date warranty ) {
    assert warranty != null;
    this.warranty = new Date( warranty.getTime() )
  }
}
Dave Jarvis
quelle
4
Bitte nehmen Sie mein Kühlschrank-Beispiel nicht zu ernst. :) Ein echter Kühlschrank sollte ein Containerobjekt sein. Ich würde erwarten, dass er weiß, wie man Objekte hält, ohne sich Gedanken darüber zu machen, was genau sie sind. IE wäre es wie eine ArrayList. Was es zu einem Kühlschrank macht, nehmen wir an, es serialisiert Objekte auf der Festplatte, wenn sie gespeichert werden, damit sie einen Systemausfall überleben. Okay. Genug, um mein Kühlschrankbeispiel ernst zu nehmen.
Winston Ewert
6
Aber sollte der Kühlschrank nicht die Verfallsdaten kennen, damit er Ihnen sagen kann, dass der Käse schlecht geworden ist und weggeworfen werden sollte? :) Ok, wir müssen damit aufhören! :)
Dima
10
Eine interessante Diskussion über Fridge Logic, die Sie hier führen ...
Mason Wheeler,
35
Haha, ich würde gerne die Werbung dafür sehen: "Es ist eine brandneue Art von Kühlschrank: Es wirft Dinge auf dich, wenn du versuchst, etwas zu ergreifen, das nicht da ist! Auf diese Weise wirst du das nur einmal versuchen! Du machst dir Sorgen stopfe den Kühlschrank und lass den Kühlschrank sich wegen illegalem Verhalten Sorgen machen! "
gablin
42
Das Beispiel hat alles falsch. Es sollte weder ein Age-Feld noch eine setAge () -Methode geben. Das Alter ist eine Funktion des Geburtsdatums der Person im Vergleich zu einem bestimmten Zeitpunkt. Dies scheint zwar trivial zu sein, ist aber genau das, was an der modernen Praxis der vollständigen Veränderbarkeit von Objekten und anderen schlechten Entwürfen falsch ist, wenn Get / Set-Methoden für private Felder verwendet werden und sorgfältig geprüft wird, was wirklich veränderlich ist, was ein Feld ist, was das Objekt sollte wissen, wie es sich verhält und welche Statusänderungen tatsächlich gültig sind (welche setX-Methoden vollständig zerstören).
Darrell Teague
41

Der Grund für Getter und Setter in Java ist sehr einfach:

  • Sie können in einer Schnittstelle nur Methoden und keine Felder angeben .

Wenn Sie also zulassen möchten, dass ein Feld die Schnittstelle passiert, benötigen Sie eine Reader- und eine Writer-Methode. Diese werden traditionell als getX und setX für das Feld x bezeichnet.

user1249
quelle
44
Das ist eine Sprachbeschränkung. Die eigentliche Frage ist, ob wir anderen Objekten erlauben sollen, diesen Zustand zu manipulieren.
Winston Ewert
3
@Peter Turner, eindeutig hindert nichts eine Sprache daran, Schnittstellen zu haben, die Eigenschaften enthalten. Unter dem Deckmantel, der möglicherweise als Getter / Setter implementiert ist, wäre es jedoch einfach genug, die Schnittstellendefinitionen für Eigenschaften zu unterstützen.
Winston Ewert
2
@ Winston, Sie müssen eventuell zulassen, dass Klassen Informationen untereinander austauschen, um die eigentliche Arbeit zu erledigen. Was würden Sie stattdessen vorschlagen?
6
Ein Objekt soll ihm eine übergeordnete Schnittstelle bieten. Getter und Setter sind in der Regel eine Low-Level-Schnittstelle. Angenommen, Sie haben eine Klasse, die einen Binärbaum implementiert. Sie können Funktionen wie GetLeft (), GetRight (), GetValue () und GetKey () verwenden, um in der Baumstruktur zu navigieren. Das ist aber der völlig falsche Ansatz. Ihre Binärbaumklasse sollte Operationen wie Suchen, Einfügen, Löschen bereitstellen.
Winston Ewert
6
Ein weiteres Beispiel ist ein Tetris-Stück. Das Tetris-Teil hat einen internen Status wie block_type, rotation, position. Sie könnten Getter wie GetBlockType (), GetRotation (), GetPosition () haben. Aber das solltest du wirklich nicht. Was Sie haben sollten, ist eine GetSquares () - Methode, die eine Liste aller von diesem Teil belegten Quadrate zurückgibt. Du solltest auch keine Sachen wie SetPosition () oder SetRotation () haben. Stattdessen sollten Sie Operationen wie MoveLeft (), MoveRight (), Rotate (), Fall () haben.
Winston Ewert
20

Von http://www.adam-bien.com/roller/abien/entry/encapsulation_violation_with_getters_and

JavaBean-Stil:

connection.setUser("dukie");
connection.setPwd("duke");
connection.initialize();

OO-Style:

connection.connect("dukie","duke");

Klar, ich bevorzuge den letzteren Ansatz. Es werden keine Implementierungsdetails ausgeblendet, es ist einfacher und präziser, und alle erforderlichen Informationen sind im Methodenaufruf enthalten, sodass es einfacher ist, die richtigen Informationen zu finden. Ich bevorzuge es auch, private Member mithilfe von Parametern im Konstruktor festzulegen, wann immer dies möglich ist.

Ihre Frage ist, wann ein Getter / Setter gerechtfertigt ist? Möglicherweise, wenn ein Moduswechsel erforderlich ist oder Sie ein Objekt nach Informationen abfragen müssen.

myObject.GetStatus();
myObject.SomeCapabilitySwitch = true;

Als ich darüber nachdachte, als ich mit dem Codieren in C # anfing, schrieb ich viel Code in dem oben dargestellten Javabeans-Stil. Aber als ich Erfahrung in der Sprache gesammelt hatte, begann ich, mehr Elemente im Konstruktor festzulegen und Methoden zu verwenden, die eher dem obigen OO-Stil ähnelten.

Robert Harvey
quelle
6
Warum ist der OO-Stil "Alle Argumente als Parameter angeben"?
5
Während Ihr "OO-Stil" für 2 oder 3 Optionen in Ordnung ist, würde ich den JavaBean-Stil vorziehen, sobald Sie darüber hinausgehen. 20 überladene Methoden für jede mögliche Kombination von Parametern zu haben, sieht einfach schrecklich aus
TheLQ
1
@TheLQ: C # 4.0 erleichtert dies, indem optionale und benannte Parameter in Konstruktor- und Methodenaufrufen zugelassen werden.
Robert Harvey
7
@TheLQ Dafür gibt es Builder mit flüssiger Syntax, siehe zB Guavas MapMaker .
Maaartinus
5
@ Thorbjørn Ravn Andersen, ich würde sagen, "Alle Argumente als Parameter angeben" ist besser im OO-Stil als "JavaBean-Setter aufrufen", da ein Setter impliziert, dass ein zugrunde liegendes Attribut festgelegt wird, das per Definition interne Details verfügbar macht. Bei Verwendung der connect (..) -Methode im Beispiel wird keine solche Annahme getroffen. Vielleicht haben Sie ein Benutzer- und ein Kennwortattribut festgelegt, vielleicht auch nicht. Wichtig ist, dass Sie sich auf das richtige OO-Konzept konzentrieren: die "Connect" -Meldung.
Andres F.
13

Wann sind Getter und Setter gerechtfertigt?

Wenn das Verhalten "get" und "set" tatsächlich mit dem Verhalten in Ihrem Modell übereinstimmt, ist dies praktisch nie der Fall .

Jede andere Verwendung ist nur ein Betrug, da das Verhalten der Geschäftsdomäne nicht klar ist.

Bearbeiten

Diese Antwort hätte leichtfertig wirken können, lassen Sie mich also näher darauf eingehen. Die obigen Antworten sind größtenteils richtig, konzentrieren sich jedoch auf die Programmierparadigmen des OO-Designs und einige, die das Gesamtbild verfehlen. Nach meiner Erfahrung denken die Leute, dass das Vermeiden von Gettings und Setters eine akademische Regel für OO-Programmiersprachen ist (z. B. denken die Leute, dass das Ersetzen von Getters durch Properties großartig ist).

Tatsächlich ist es eine Konsequenz des Designprozesses. Sie müssen sich nicht mit Schnittstellen, Kapselung und Mustern befassen und darüber streiten, ob diese Paradigmen durchbrochen werden oder nicht und was eine gute OO-Programmierung ist oder nicht. Der einzige Punkt, der letztendlich wichtig ist, ist, dass Sie Ihre Domain nicht modellieren, wenn sich in Ihrer Domain nichts befindet, das so funktioniert, indem Sie es in Sie einfügen.

Die Realität ist, dass es sehr unwahrscheinlich ist, dass ein System in Ihrem Domänenbereich Getter und Setter hat. Sie können nicht zu dem Mann oder der Frau gehen, der bzw. die für die Gehaltsabrechnung verantwortlich ist, und einfach "Setzen Sie dieses Gehalt auf X" oder "Holen Sie mir dieses Gehalt" . Ein solches Verhalten gibt es einfach nicht

Wenn Sie dies in Ihren Code einfügen, entwerfen Sie Ihr System nicht so, dass es mit dem Modell Ihrer Domain übereinstimmt. Ja, das bricht Schnittstellen und Kapselung, aber das ist nicht der Punkt. Der Punkt ist, dass Sie etwas modellieren, das nicht existiert.

Außerdem fehlt Ihnen wahrscheinlich ein wichtiger Schritt oder Prozess, da es wahrscheinlich einen Grund gibt, warum ich nicht einfach zu Fuß gehen kann, um zu zahlen und zu sagen, dass Sie dieses Gehalt auf X setzen.

Wenn Leute Getter und Setter benutzen, tendieren sie dazu, die Regeln für diesen Prozess an die falsche Stelle zu verschieben. Dies entfernt sich noch mehr von Ihrer Domain. Am Beispiel der realen Welt ist es wie ein Gehalt, wenn man annimmt, dass die zufällige Person, die hereinkommt, die Erlaubnis hat, diese Werte zu erhalten, sonst würde sie nicht danach fragen. Es ist nicht nur so, wie die Domain ist, es ist auch so, wie die Domain ist.

Cormac Mulhall
quelle
Dies scheint nichts Wesentliches über die Punkte zu bieten, die in den vorherigen 17 Antworten gemacht und erklärt wurden
Mücke
1
Die anderen Antworten sprechen über Objekte, Schnittstellen und andere Sprachparadigmen. Was in Ordnung ist, aber es ist nicht das zentrale Thema. Getter und Setter sind nicht nur schlecht, weil sie gegen einige Kodierungskonventionen verstoßen, sondern vor allem, weil Sie dieses Verhalten in dem Modell, das Sie darstellen, kaum jemals feststellen. Dieser Punkt geht in der Diskussion über die besten Codierungsmethoden verloren.
Cormac Mulhall
Was ist Ihrer Meinung nach die Schnittstelle zur Lohn- und Gehaltsabrechnung, wenn es sich nicht um ein set/getGehalt handelt?
Winston Ewert
1
Wenn sie um Berechtigungsebenen gewickelt und auf bestimmte externe Entitäten beschränkt sind, sind sie keine Getter und Setter. Ich sage nicht, dass es in der Personalabteilung keine Methode gibt, die das Gehalt ändert, aber diese Methode sollte mit den internen Prozessen im Unternehmen selbst in Einklang stehen. Beispielsweise legen die meisten Unternehmen das Gehalt auf der Grundlage des von einem Manager genehmigten Mitarbeitervertrags fest. Wenn sich das Gehalt des Mitarbeiters ändert, wird ein neuer Vertrag erstellt, den ein Manager unterschreiben muss. Daher sollte die Lohn- und Gehaltsabrechnung einen Vertragsgegenstand und einen Berechtigungsgegenstand erwarten, um die Lohn- und Gehaltsliste zu ändern
Cormac Mulhall
3
Anders ausgedrückt, es besteht ein ziemlicher Unterschied zwischen set_salary (new_salary, employee_id) und authorized_salary_update (employee_contract, authorising_manager). Der Prozess sollte den internen Geschäftsprozess modellieren
Cormac Mulhall
11

Getter und Setter sind in der Regel eine schlechte Idee. Wenn ein Feld nicht logisch Teil der Schnittstelle ist und Sie es als privat kennzeichnen, ist das in Ordnung. Wenn es logisch Teil der Schnittstelle ist und Sie es öffentlich machen, ist das in Ordnung. Aber wenn Sie es privat machen und dann umdrehen und es durch Bereitstellen eines Get- und Setters wieder effektiv veröffentlichen, kehren Sie zu Ihrem Ausgangspunkt zurück, mit der Ausnahme, dass Ihr Code jetzt ausführlicher und verschleierter ist.

Offensichtlich gibt es Ausnahmen. In Java müssen Sie möglicherweise Schnittstellen verwenden. Die Anforderungen an die Abwärtskompatibilität der Java-Standardbibliothek sind so hoch, dass sie normale Maße für die Codequalität überwiegen. Es ist sogar möglich, dass Sie es tatsächlich mit dem legendären, aber seltenen Fall zu tun haben, in dem es eine gute Chance gibt, dass Sie später ein gespeichertes Feld durch eine Sofortberechnung ersetzen, ohne die Benutzeroberfläche anderweitig zu beschädigen. Aber das sind Ausnahmen. Getter und Setter sind ein Anti-Pattern, das einer besonderen Begründung bedarf.

Wallace
quelle
6
Getter und Setter bedeuten, dass das Attribut und nicht das Feld Teil der Schnittstelle ist. Angenommen, getBirthDate () und SetBirthDate () nehmen "yyyy / mm / dd" an, aber das gespeicherte Feld gibt die Anzahl der Tage seit dem 01.01.1000 an. Sie können dies in 01/01/0001 ändern, und der Getter und der Setter behalten die Schnittstelle bei. Insbesondere, weil "öffentlich" öffentlich verwerflich ist, sollten Variablen niemals öffentlich sein. Verwenden Sie immer einen Setter, um den eingehenden Wert zu validieren, verwandte Felder zu aktualisieren, nach Bedarf zu konvertieren usw. Versuchen Sie, einen Getter zu verwenden, falls sich der interne Speicher ändert.
Andy Canfield
@AndyCanfield, das ist ein bisschen stark zu sagen, dass öffentlich öffentlich müllbar ist. Das ist nur so, wenn du es schlecht programmierst. Wenn Sie einen Setter haben und der falsche Wert an ihn gesendet wird, können Sie eine Ausnahme auslösen, die den Papierkorb des Programms aufruft, aber mit einem Fehler beendet wird Wert dann können Sie das im Hinterkopf behalten und es testen. Sie können sagen "ah, aber ich muss nur 1 Test durchführen, wenn ich einen Setter verwende", aber möglicherweise gibt Ihr eigener Code der Variablen einen schlechten Wert Reduzieren Sie Ihre tests.n mit Tests, Ihr Prog ist nicht "Trashed"
Barlop
3

ob das Feld direkt oder über die Methode zugänglich ist, ist nicht wirklich wichtig.

Klasseninvarianten (nützliche) sind wichtig. Und um sie zu erhalten, müssen wir manchmal nicht in der Lage sein, etwas von außen zu ändern. Z.B. Wenn wir eine Klasse Square mit getrennter Breite und Höhe haben, wird sie durch Ändern einer von ihnen zu etwas anderem als einem Quadrat. Wir brauchen also die Methode changeSide. Wenn es Rectangle wäre, könnten wir Setter / public field haben. Aber Setter als würde testen, ob seine größer als Null besser wäre.

Und in konkreten Sprachen (zB Java) sind Gründe, warum wir diese Methoden (Schnittstellen) brauchen. Ein weiterer Grund für die Methode ist die Kompatibilität (Quelle und Binärdatei). Es ist also einfacher, sie hinzuzufügen, als zu überlegen, ob ein öffentliches Feld ausreichen würde.

btw. Ich verwende gerne einfache Klassen mit unveränderlichem Wert und öffentlichen Endfeldern.

user470365
quelle
Dies ist so ziemlich das einzige, was vermieden werden sollte. Der Wechsel von Feld zu Eigenschaft / Getter / Setter ist in den meisten Sprachen ein bahnbrechender Wechsel.
MrDosu
2

Möglicherweise möchten Sie Ihre Interna in einen beliebigen Wert ändern, während die Schnittstellen unverändert bleiben. Wenn sich Ihre Schnittstellen nicht ändern, wird der Code nicht beschädigt. Sie können Ihre Interna immer noch ändern, wie Sie möchten.

George Silva
quelle
1
Ich bin überrascht, dass diese Antwort so weit unten ist. Mit Gettern und Setzern können Sie in Zukunft mehr tun (z. B. ein Ereignis auslösen, mehr Eingabevalidierung durchführen, interne Buchhaltung betreiben), ohne Ihre vorhandene API zu beschädigen. Selbst bei nicht öffentlichem Code sind die zu aktualisierenden APIs umso seltener, je weniger Abhängigkeiten zu aktualisieren sind und je weniger Fehler bei diesen Aktualisierungen aufgetreten sind.
AmadeusDrZaius
0

Mein Ansatz ist dies -

Wenn ich später mit den Daten rechne, ist ein Getter / Setter gerechtfertigt. Wenn sich etwas ändert, schiebe ich die Daten oft in einen Getter / Setter.

Wenn es sich um eine POD-Struktur handelt, lasse ich die Slots frei.

Auf einer abstrakteren Ebene lautet die Frage "Wer verwaltet die Daten?" Und das hängt vom Projekt ab.

Paul Nathan
quelle
0

Wenn sich die Verwendung von Gettern und Setzern kompliziert anfühlt, liegt das Problem möglicherweise in der Sprache und nicht im eigentlichen Konzept.

Hier ist der Code aus dem zweiten Beispiel in Ruby:

class Fridge
  attr_accessor :cheese
end

def go_shopping fridge
  fridge.cheese += 5
end

Beachten Sie, dass es dem ersten Beispiel in Java sehr ähnlich sieht ? Wenn Getter und Setter als erstklassige Bürger behandelt werden, sind sie keine lästige Arbeit, und die zusätzliche Flexibilität kann manchmal ein echter Segen sein. Beispielsweise könnten wir beschließen, einen Standardwert für Käse in einem neuen Kühlschrank zurückzugeben:

class Fridge
  attr_accessor :cheese

  def cheese
    @cheese || 0
  end
end

Natürlich wird es viele Variablen geben, die überhaupt nicht öffentlich zugänglich gemacht werden sollten. Wenn Sie interne Variablen unnötig offenlegen, wird Ihr Code schlechter, aber Sie können Gettern und Settern kaum die Schuld geben.

Tobias Cohen
quelle
Das ist wunderschön gesagt. Builder-Funktionen wurden hinzugefügt, weil wir unveränderliche Objekte erstellen möchten, während das Rezept schrittweise aufgebaut wird: Wenn Sie künstlichen Süßstoff hinzufügen, müssen Sie möglicherweise keinen Zucker hinzufügen. Die Verwendung benannter Parameter und selbst bei Überladung ist es sehr schwierig, eine Methode zu erstellen, die alle Aufgaben erfüllt. Und dann hat Java natürlich keine benannten Parameter, daher gibt es einen weiteren Grund, warum Leute das Builder-Muster verwenden.
YoYo
0

Betrachten Sie eine SizeKlasse, die Breite und Höhe einschließt. Ich kann Setter mit dem Konstruktor eliminieren, aber wie hilft mir das beim Zeichnen eines Rechtecks Size? Breite und Höhe sind keine internen Daten für die Klasse. Es handelt sich um gemeinsam genutzte Daten, die den Verbrauchern von Size zur Verfügung stehen müssen.

Objekte bestehen aus Verhalten und Zustand - oder Attributen. Wenn es keinen exponierten Zustand gibt, sind nur die Verhaltensweisen öffentlich.

Wie würden Sie ohne Staat eine Sammlung von Objekten sortieren? Wie würden Sie nach einer bestimmten Instanz eines Objekts suchen? Was passiert, wenn Sie nur Konstruktoren verwenden, wenn Ihr Objekt über eine lange Liste von Attributen verfügt?

Kein Methodenparameter sollte jemals ohne Validierung konsumiert werden. Deshalb ist es faul zu schreiben:

setMyField(int myField){
    this.myField = myField;
}

Wenn Sie es so schreiben, haben Sie sich zumindest darauf vorbereitet, den Setter für die Validierung zu verwenden. es ist besser als ein öffentliches Feld - aber gerade noch. Zumindest verfügen Sie jedoch über eine konsistente öffentliche Schnittstelle, über die Sie Validierungsregeln festlegen können, ohne den Code Ihrer Kunden zu verletzen.

Getter, Setter, Eigenschaften, Mutatoren, nennen Sie sie, wie Sie wollen, aber sie sind notwendig.

Dale
quelle
Zumindest verfügen Sie jedoch über eine konsistente öffentliche Schnittstelle, über die Sie Validierungsregeln festlegen können, ohne den Code Ihrer Kunden zu verletzen. Das ist nicht wahr. Das Hinzufügen von Validierungsregeln könnte sehr wohl "den Code Ihrer Kunden brechen". Nehmen Sie zum intBeispiel eine Instanzvariable und stellen Sie sich vor, Sie möchten eine Validierungsregel hinzufügen, um nur nicht negative Werte zu akzeptieren. Das Problem ist, dass der Code Ihres Kunden möglicherweise bereits auf der Möglichkeit beruht, ihn auf einen negativen Wert zu setzen ...
jub0bs
0

Wenn Getter und Setter die Kapselung und die wahre OO verletzen, dann habe ich ernsthafte Probleme.

Ich hatte immer das Gefühl, dass ein Objekt das darstellt, woran man am besten denkt, dass man es braucht.

Ich habe gerade ein Programm geschrieben, das Mazes in Java generiert, und ich habe eine Klasse, die "Maze Squares" repräsentiert. Ich habe Daten in dieser Klasse, die Koordinaten, Wände und Boolesche Werte usw. darstellen.

Ich muss eine Möglichkeit haben, diese Daten zu ändern / zu manipulieren / darauf zuzugreifen! Was mache ich ohne Getter und Setter? Java verwendet keine Eigenschaften und das Festlegen aller meiner Daten, die für diese Klasse lokal sind, als öffentlich ist definitiv ein Verstoß gegen die Kapselung und OO.

Bryan Harrington
quelle
6
Hier ist die Frage: Wenn Sie alle diese Felder öffentlich machen und die Verwendung der Getter und Setter einstellen würden, wie würde sich Ihr Code unterscheiden?
Winston Ewert
In der Theorie ist es nicht. Warum an diesem Punkt überhaupt eine Klasse machen? Dasselbe Argument könnte mit allem gemacht werden. Aber der Beitrag unter mir scheint es eleganter gesagt zu haben. (Getter und Setter sind eine Möglichkeit, einen Wert sicher an ein Objekt weiterzugeben oder einen Wert von diesem Objekt abzurufen.) Der Post geht weiter.
Bryan Harrington
3
Ich möchte darauf hinweisen: Dieses Plakat stimmte mir schließlich zu und veröffentlichte eine weitere Antwort auf diese Frage. Die Verwendung von Gettern und Setzern entspricht im Wesentlichen der direkten Manipulation der Daten. Sie gewinnen nicht wirklich OOness, indem Sie das tun. Um OO zu sein, sollten Sie eine übergeordnete Schnittstelle zu den Daten bereitstellen.
Winston Ewert
4
Ich denke, die beste Art zu beschreiben wäre zu sagen, dass Sie Ihre Schnittstellen von außen nach innen und nicht von innen nach außen gestalten sollten. Die Schnittstelle, die Ihr Objekt bereitstellt, sollte davon bestimmt werden, wie das Objekt verwendet und nicht wie es implementiert wird. Schreiben Sie also keine Getter und lassen Sie externe Objekte die Daten interpretieren. Finden Sie heraus, welche Frage beantwortet werden muss, und schreiben Sie eine Methode, die diese Frage beantwortet. Erlauben Sie externen Objekten nicht, den Status durch einen Setter zu ändern. Finden Sie heraus, welche Manipulationen sie ausführen müssen, und schreiben Sie Methoden, die dies tun.
Winston Ewert
2
In einigen Fällen sind Getter und Setter die beste Methode. Manchmal sind sie die beste Schnittstelle, die Sie bereitstellen können. In vielen Fällen ist dies jedoch nicht der Fall. In vielen Fällen leiten Sie Implementierungsdetails nur an andere Klassen weiter.
Winston Ewert
-1

Erstens gibt es im Großen und Ganzen zwei Arten von Objekten, die ich kommentieren werde: Wert- und Servicetypen.

Diensttypen sollten niemals Setter haben. Welche Abhängigkeiten auch immer sie benötigen, sollten nicht abrufbar sein. Die beste Möglichkeit, Abhängigkeiten zu übergeben, besteht in der Verwendung eines Konstruktors oder einer Factory. Auf diese Weise werden alle Instanzen von Anfang an vollständig gebildet.

Werttypen sollten auch unveränderlich sein. Andererseits gibt es Zeiten, in denen dies nicht praktikabel ist, wie z. B. ein ORM oder eine andere Zuordnung, z. B. von einem Widget zu einem Objekt. Alle anderen Werttypen, die vom einen Layer oder Teil zum anderen um das System herum übertragen werden, sollten unveränderlich sein und keine Setter haben.

mP01
quelle
Ich würde einen dritten Typ vorschlagen: einen veränderlichen Container, der einen oder mehrere Werte enthalten soll; In vielen Fällen sollte von einem Container nicht viel erwartet werden, was Logik oder Validierung angeht. Code, der eine Methode zur Berechnung von sechs positiven Ganzzahlen benötigt, kann einen Container für sechs Ganzzahlen an die Methode übergeben, in der die Ergebnisse gespeichert werden. Die Verantwortung dafür, dass die Zahlen positiv sind, sollte im Allgemeinen beim Aufrufer oder der aufgerufenen Methode liegen, nicht beim Container.
Supercat
-1

Ein Getter / Setter ist wie jede andere öffentliche Methode gerechtfertigt. Die Rechtfertigung liegt hauptsächlich darin, dass wir eine Schnittstelle zur Außenwelt bereitstellen möchten, die definiert, wie unsere Klasse mit anderen Klassen interagiert. Wir tun dies hauptsächlich, weil wir die Kopplung zwischen getrennten Einheiten reduzieren wollen. Das Reduzieren der Kopplung ist aus vielen Gründen eine gute Sache:

  • Ich kann aus meinem aktuellen Code dieselbe Klassenschnittstelle verwenden, obwohl die Klasse inzwischen neue Methoden hinzugefügt hat (aber die alte Schnittstelle beibehalten hat).
  • Kann die interne Darstellung einer Klasse ändern, ohne die Konsumenten zu beeinflussen
  • Reduzieren Sie das Risiko von Fehlern: Wenn Sie Ihre internen Daten als privat kennzeichnen, können Sie sicher sein, dass externer Code nicht mit Ihren internen Daten in Konflikt gerät
Ghita
quelle
-1

Ja, Getter und Setter sind ein Anti-Pattern in der objektorientierten Programmierung und sollten niemals verwendet werden: http://www.yegor256.com/2014/09/16/getters-and-setters-are-evil.html . Kurz gesagt, sie passen nicht in ein objektorientiertes Paradigma, weil sie Sie ermutigen, ein Objekt wie eine Datenstruktur zu behandeln. Welches ist ein großer Irrtum.

yegor256
quelle
2
dies scheint nicht alles zu bieten erhebliche über Punkte gemacht und erläutert vor 18 Antworten
gnat
4
Es ist auch ein bisschen stark, nie zu sagen .
Winston Ewert
-4

Ich hoffe, dass alles, was die von der Firma, die Ihre Programmiersprache entwickelt, entwickelte IDE automatisch generiert, nicht gegen die "Prinzipien der Objektorientierung" verstößt.

Das heißt, wenn Sie eine Variable mit öffentlichem Gültigkeitsbereich haben, verwenden Sie einen Getter und einen Setter, und Sie sind golden. Scheint für mich das notwendige Element des extrem objektorientierten Kapselungsprinzips zu sein - auch wenn es nur nach viel Junk-Code aussieht.

Der Grund für die Verwendung ist, dass die ordnungsgemäße Verkapselung möglicherweise noch stattfindet. Solange Sie die Ebene entfernt haben, mit der die Person, die mit Ihrem Objekt spielt, Ihr Objekt fühlt, können Sie das Objekt entfernen, ohne dass er es überhaupt weiß, oder?

Es genügt zu sagen, ein geteiltes Haus kann nicht bestehen, einer der Mieter von OO wird sich nicht selbst anmachen, und Sie können nicht sowohl der Kapselung als auch der vorzeitigen Optimierung dienen.

Peter Turner
quelle
4
und was genau habe ich gewonnen, wenn ich jedes Mal, wenn ich mich mit der Variablen befasse, viel Code hinzugefügt habe, um setFoo () und getFoo () aufzurufen?
Winston Ewert
7
Hier ist das Ding, ich glaube nicht, dass du eine Kapselung bekommst. Sie bearbeiten den Status des Objekts immer noch von anderen Orten aus. Du machst es nur wortgewaltiger. Um eine Kapselung zu erhalten, müssen Sie die Manipulation von Werten in der Klasse, deren Eigentümer dieser Wert ist, zentralisieren. Alles andere ist nur ein Verfahrenscode in OO-Kleidung. (Daran ist nicht unbedingt etwas auszusetzen, aber es ist das, was es ist).
Winston Ewert
2
Der Vorteil der Kapselung besteht darin, dass sich die gesamte Logik, die sich auf bestimmte Werte bezieht, an einer Stelle befindet (mehr oder weniger). Ich verstehe das nicht mit Gettern und Setzern. Daher glaube ich nicht, dass ich viel Nutzen für sie habe.
Winston Ewert
1
Ich stimme jedoch zu, dass ich lieber Code mit Gettern und Setzern als einen Code haben möchte, der Daten über den öffentlichen Zugriff frei manipuliert. Auf diese Weise kann ich zumindest eine Validierung der eingehenden Daten anhängen.
Winston Ewert
3
Ich bin sicher kein Fan von Ausführlichkeit. Aber mein Einwand sind wirklich Leute, die ihre Variablen privat machen und dann Getter und Setter für sie aufrufen, was so gut wie keinen Unterschied ergibt. Wenn Sie Getter / Setter in Ihrer Klasse verwenden möchten, ist das in Ordnung. Die Frage ist warum? Der größte Vorteil, der mir bekannt ist, besteht darin, dass Sie die Daten überprüfen können, um Fehler früher zu erkennen. Dies ist weniger hilfreich, wenn sich der gesamte Code, mit dem die Daten bearbeitet werden, an derselben Stelle befindet. Aber es kann trotzdem hilfreich sein. Im Idealfall haben Sie eine Sprache, die Eigenschaften unterstützt und die Ausführlichkeit vermeiden kann.
Winston Ewert
-4

Ich dachte, wir hätten hier üblichere Antworten?

Getter und Setter sind im Allgemeinen extrem OOP (jeder, der etwas anderes sagt, ist falsch, so einfach ist das).

Obwohl Sie sich daran erinnern müssen, nicht zu viel zu entwickeln, sollten Sie niemals einen Getter oder Setter erstellen, der nicht benötigt wird. Alle Informationen, die Sie nicht von einer anderen Klasse verwenden werden, für die Sie keinen Getter oder Setter haben sollten.

Der Grund, warum Sie Felder nicht veröffentlichen und stattdessen Getter und Setter haben, ist meistens:

  • Es ist nicht möglich, ordnungsgemäße Tests mithilfe von Feldern durchzuführen. Getter / Setter sind in dieser Hinsicht viel besser.

  • Es ist unmöglich, Felder in einer Echtzeit-Programmiereinstellung richtig zu verwenden. Synchronisierte Getter / Setter sind der richtige Weg.

  • Das Oberflächenthema (bereits erklärt, damit ich es nicht tue).

  • Es ist einfacher zu verwenden, wenn das System ordnungsgemäß wiederverwendet wird.

  • Es ist viel schwieriger zu wissen, welche Klassen Ihre Klasse verwenden und was beim Ändern eines Feldes kaputt geht als ein Getter / Setter.

Aus diesem Grund verwenden Sie ein öffentliches Feld nur dann, wenn Sie kein offizielles Projekt erstellen, sondern es nur zum Spaß tun und sich nicht um die Getter / Setter kümmern.

Skarion
quelle
2
+1 für die Testbarkeit. Schockiert, dass niemand es früher erwähnt hat. Ein weiteres nützliches Merkmal von Eigenschaften ist das Debuggen. (Es ist viel einfacher, einen Haltepunkt in den Code einzufügen, als Hardware- / Speicherhaltepunkte für Felder zu benötigen).
Mark H
9
Der Einwand gegen Getter und Setter ist, dass sie oft verwendet werden, um den Buchstaben des OOP-Gesetzes zu befolgen und dabei den Geist und die Absicht zu ignorieren. OOP sagt, dass Sie nicht zulassen sollten, dass andere Objekte sich mit Ihren Einbauten vermischen. Wenn Sie Getter und Setter definieren, mit denen Sie dies trotzdem tun können, verletzen Sie am Ende die OOP.
Winston Ewert
6
Ich stimme zu, dass Getter und Setter gegenüber einfachen öffentlichen Feldern viele Vorteile bieten. Mir ist jedoch nicht klar, wie das Testen mit ihnen besser ist. Könntest du erklären?
Winston Ewert
4
@ Karion: Ich stimme deiner Antwort nicht zu. Sie scheinen zwei Alternativen vorzustellen: entweder Getter / Setter verwenden oder Felder öffentlich machen. Es gibt jedoch eine dritte Option: Verwenden Sie weder Getter / Setter noch Belichten von Feldern! Warum müssten Sie den Zustand eines internen Feldes testen? Sollten Sie nicht die öffentliche API Ihres Objekts testen?
Andres F.