1878 Stimmen

Warum Getter und Setter/Accessors verwenden?

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

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

public String foo;

schlechter ist als:

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

Ersteres hingegen erfordert viel weniger Standardcode.

8 Stimmen

10 Stimmen

Natürlich ist beides gleich schlecht, wenn das Objekt keine zu ändernde Eigenschaft benötigt. Ich würde lieber alles privat machen, und dann Getter hinzufügen, wenn es nützlich ist, und Setter, wenn es nötig ist.

31 Stimmen

Google "accessors are evil"

57voto

Peter D Punkte 4801

Dafür gibt es viele Gründe. Mein Lieblingsgrund ist, wenn Sie das Verhalten ändern oder regeln müssen, was Sie auf eine Variable setzen können. Nehmen wir zum Beispiel an, Sie hätten eine Methode setSpeed(int speed). Sie möchten aber, dass Sie nur eine Höchstgeschwindigkeit von 100 einstellen können. Sie würden etwas tun wie:

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

Was wäre nun, wenn Sie überall in Ihrem Code das öffentliche Feld verwenden und dann feststellen, dass Sie die obige Anforderung benötigen? Viel Spaß bei der Suche nach jeder Verwendung des öffentlichen Feldes, anstatt nur Ihren Setter zu ändern.

Meine 2 Cents :)

78 Stimmen

Jede Verwendung des öffentlichen Feldes aufzuspüren, sollte nicht so schwer sein. Machen Sie es privat und lassen Sie den Compiler sie finden.

12 Stimmen

Das stimmt natürlich, aber warum sollte man es noch schwieriger machen, als es eigentlich sein sollte. Der get/set-Ansatz ist immer noch die bessere Lösung.

38 Stimmen

@GraemePerrow sie alle ändern zu müssen, ist ein Vorteil, kein Problem :( Was wäre, wenn Sie einen Code hätten, der davon ausgeht, dass die Geschwindigkeit höher als 100 sein kann (weil, Sie wissen schon, bevor Sie den Vertrag gebrochen haben, konnte sie das!) ( while(speed < 200) { do_something(); accelerate(); } )

41voto

Thomas Owens Punkte 110966

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

Zum Beispiel, wenn foo öffentlich war, konnte ich es einfach auf null und dann könnte jemand anderes versuchen, eine Methode des Objekts aufzurufen. Aber es ist nicht mehr da! Mit einer setFoo Methode konnte ich sicherstellen, dass foo wurde nie auf null .

Accessoren und Mutatoren ermöglichen auch die Kapselung - wenn Sie den Wert nicht sehen sollen, sobald er gesetzt ist (vielleicht wird er im Konstruktor gesetzt und dann von Methoden verwendet, aber er soll nie geändert werden), wird er nie von jemandem gesehen. Wenn Sie jedoch anderen Klassen erlauben können, ihn zu sehen oder zu ändern, können Sie den entsprechenden Accessor und/oder Mutator bereitstellen.

30voto

Danke, das hat mir wirklich Klarheit verschafft. Jetzt ist hier (fast) 10 (fast) gute Gründe NICHT zu verwenden, Getter und Setter:

  1. Wenn Sie merken, dass Sie mehr tun müssen als nur den Wert zu setzen und zu holen, können Sie das Feld einfach privat machen, was Ihnen sofort zeigt, 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 Aufrufer Ihnen einen Wert übergibt, den Sie [Schock Horror] so speichern sollen, wie er ist.

  4. Sie können die interne Darstellung ausblenden - fantastisch, Sie stellen also sicher, dass all diese Operationen symmetrisch sind, richtig?

  5. Sie haben Ihre öffentliche Schnittstelle gegen Änderungen unter der Folie isoliert - wenn Sie eine Schnittstelle entworfen haben und sich nicht sicher waren, ob ein direkter Zugriff auf etwas in Ordnung ist, hätten Sie weiter entwerfen sollen.

  6. Einige Bibliotheken erwarten dies, aber nicht viele - Reflection, Serialisierung, Mock-Objekte funktionieren alle sehr gut mit öffentlichen Feldern.

  7. Wenn Sie diese Klasse erben, können Sie die Standardfunktionalität außer Kraft setzen - mit anderen Worten, Sie können den Aufrufer wirklich verwirren, indem Sie nicht nur die Implementierung verbergen, sondern sie auch inkonsistent machen.

Die letzten drei lasse ich einfach stehen (N/A oder D/C)...

14 Stimmen

Ich denke, das ist das entscheidende Argument, "Wenn man eine Schnittstelle entwirft und sich nicht sicher ist, ob ein direkter Zugriff auf etwas in Ordnung ist, dann hätte man weiter entwerfen sollen." Das ist das wichtigste Problem bei Gettern/Settern: Sie reduzieren eine Klasse auf einen bloßen Container von (mehr oder weniger) öffentlichen Feldern. In real In der OOP ist ein Objekt jedoch mehr als nur ein Container mit Datenfeldern. Es kapselt den Zustand und die Algorithmen zur Manipulation dieses Zustands. Das Entscheidende an dieser Aussage ist, dass der Zustand sein soll gekapselt und nur durch die vom Objekt bereitgestellten Algorithmen manipuliert werden.

29voto

jcdyer Punkte 17892

Das hängt von Ihrer Sprache ab. Sie haben dies als "objektorientiert" und nicht als "Java" bezeichnet, daher möchte ich darauf hinweisen, dass die Antwort von ChssPly76 sprachabhängig ist. In Python zum Beispiel gibt es keinen Grund, Getter und Setter zu verwenden. Wenn Sie das Verhalten ändern müssen, können Sie eine Eigenschaft verwenden, die einen Getter und Setter um den grundlegenden Attributzugriff wickelt. Etwa so:

 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 Stimmen

Ja, das habe ich bereits in einem Kommentar unter meiner Antwort gesagt. Java ist nicht die einzige Sprache, die Getter / Setter als Krücke benutzt, so wie Python nicht die einzige Sprache ist, die Eigenschaften definieren kann. Der Hauptpunkt bleibt jedoch bestehen - "Eigenschaft" ist nicht dasselbe wie "öffentliches Feld".

0 Stimmen

@ChssPly76: aber der Hauptgrund für die Verwendung von Gettern und Settern ist es, zu verhindern, dass man die Schnittstelle ändern muss, wenn man mehr Funktionalität hinzufügen möchte, was bedeutet, dass es vollkommen akzeptabel - sogar idomatisch - ist, öffentliche Felder zu verwenden, bis eine solche Funktionalität benötigt wird.

1 Stimmen

@jcd - ganz und gar nicht. Sie definieren Ihre "Schnittstelle" (öffentliche API wäre hier der bessere Begriff), indem Sie Ihre öffentlichen Felder offenlegen. Sobald das geschehen ist, gibt es kein Zurück mehr. Eigenschaften sind KEINE Felder, denn sie bieten Ihnen einen Mechanismus, um Zugriffsversuche auf Felder abzufangen (indem Sie sie an Methoden weiterleiten, wenn diese definiert sind); das ist jedoch nichts anderes als Syntaxzucker über Getter-/Setter-Methoden. Das ist sehr praktisch, ändert aber nichts am zugrundeliegenden Paradigma - die Offenlegung von Feldern ohne Kontrolle über den Zugriff auf sie verstößt gegen das Prinzip der Kapselung.

28voto

Haakon Løtveit Punkte 898

EDIT: Ich habe diese Frage beantwortet, weil eine Reihe von Leuten, die das Programmieren lernen, diese Frage stellen, und die meisten Antworten sind technisch sehr kompetent, aber sie sind nicht so leicht zu verstehen, wenn man ein Neuling ist. Wir waren alle Neulinge, also dachte ich, ich versuche mich an einer anfängerfreundlicheren Antwort.

Die beiden wichtigsten davon sind Polymorphismus und Validierung. Selbst wenn es nur eine dumme 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 in ihr befindet und welches Fassungsvermögen sie hat (in Millilitern).

Was passiert, wenn ich das tue:

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

Nun, Sie würden nicht erwarten, dass das funktioniert, oder? Sie wollen, dass es eine Art Sicherheitskontrolle gibt. Und schlimmer noch, was wäre, wenn ich nie die maximale Kapazität angegeben hätte? Oh je, wir haben ein Problem.

Aber es gibt noch ein weiteres 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 unterschiedlichen Kapazitäten und Flüssigkeitsmengen? Wenn wir nur eine Schnittstelle schaffen könnten, könnten wir den Rest unseres Programms diese Schnittstelle akzeptieren lassen, und Flaschen, Kanister und alles Mögliche würden einfach austauschbar funktionieren. Wäre das nicht besser? Da Schnittstellen Methoden erfordern, ist das auch eine gute Sache.

Das Ergebnis wäre dann etwa so:

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

Großartig! Und jetzt ändern wir Bottle einfach in das hier:

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 dem Leser als Übung.

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 dieser Art von Dingen umgehen, kann ganz unterschiedlich sein. Sie können Flaschen haben, die ihren Zustand auf die Festplatte schreiben, wenn er sich ändert, oder Flaschen, die in SQL-Datenbanken oder GNU weiß was sonst noch speichern.

Und all diese können auf unterschiedliche Art und Weise mit verschiedenen Unglücken umgehen. Die Flasche prüft einfach, und wenn sie überläuft, löst sie eine RuntimeException aus. Aber das könnte der falsche Weg sein. (Es gibt eine nützliche Diskussion über die Fehlerbehandlung, aber ich halte sie hier absichtlich sehr einfach. In den Kommentaren werden die Leute wahrscheinlich auf die Schwächen dieses simplen Ansatzes hinweisen ;) )

Und ja, es scheint, dass wir von einer sehr einfachen Idee schnell zu viel besseren Antworten kommen.

Bitte beachten Sie auch, dass Sie das Fassungsvermögen einer Flasche nicht ändern können. Sie ist jetzt in Stein gemeißelt. Sie könnten dies mit einem int tun, indem Sie ihn für endgültig erklären. Aber wenn es sich um eine Liste handeln würde, könnte man sie leeren, neue Dinge hinzufügen und so weiter. Man kann den Zugriff nicht darauf beschränken, die Innereien zu berühren.

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

CodeJaeger.com

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

Powered by:

X