Lassen Sie uns zunächst das Paradigma klarstellen.
- Datenstrukturen -> Ein Layout des Speichers, das von sachkundigen Funktionen durchlaufen und bearbeitet werden kann.
- Objekte -> Ein eigenständiges Modul, das seine Implementierung verbirgt und eine Schnittstelle bereitstellt, über die kommuniziert werden kann.
Wo ist ein Getter / Setter nützlich?
Sind Getter / Setter in Datenstrukturen nützlich? Nein.
Eine Datenstruktur ist eine Speicherlayoutspezifikation, die einer Reihe von Funktionen gemeinsam ist und von diesen manipuliert wird.
Im Allgemeinen kann jede alte neue Funktion eine Datenstruktur manipulieren, wenn dies so geschieht, dass die anderen Funktionen es noch verstehen können, dann wird die Funktion der Familie hinzugefügt. Ansonsten ist es eine Schurkenfunktion und eine Fehlerquelle.
Verstehen Sie mich nicht falsch, es könnte mehrere Funktionsfamilien geben, die überall über diese Datenstruktur mit Snitches, Turncoats und Double-Agents streiten. Es ist in Ordnung, wenn jeder seine eigene Datenstruktur hat, mit der er spielen kann, aber wenn er sie teilt ... Stellen Sie sich vor, mehrere kriminelle Familien sind sich nicht einig über Politik, es kann sehr schnell zu einem Durcheinander kommen.
Gibt es eine Möglichkeit, die Datenstruktur zu verschlüsseln, damit Schurkenfunktionen nicht alles durcheinander bringen, wenn die erweiterten Funktionsfamilien dies erreichen können? Ja, sie heißen Objekte.
Sind Getter / Setter in Objekten nützlich? Nein.
Der springende Punkt beim Einbinden einer Datenstruktur in ein Objekt war, sicherzustellen, dass keine unerwünschten Funktionen vorhanden sind. Wenn die Funktion der Familie beitreten wollte, musste sie zuerst gründlich überprüft werden und dann Teil des Objekts werden.
Der Zweck eines Get- und Setters besteht darin, Funktionen außerhalb des Objekts die Möglichkeit zu geben, das Speicherlayout des Objekts direkt zu ändern. Das klingt nach einer offenen Tür, die es Schurken erlaubt ...
Der Edge-Fall
Es gibt zwei Situationen, in denen ein öffentlicher Getter / Setter Sinn macht.
- Ein Teil der Datenstruktur innerhalb des Objekts wird vom Objekt verwaltet, aber nicht vom Objekt gesteuert.
- Eine Schnittstelle, die eine allgemeine Abstraktion einer Datenstruktur beschreibt, bei der erwartet wird, dass einige Elemente nicht die Kontrolle über das implementierende Objekt haben.
Container und Containerschnittstellen sind perfekte Beispiele für diese beiden Situationen. Der Container verwaltet die Datenstrukturen (verknüpfte Liste, Karte, Baum) intern, übergibt jedoch die Kontrolle über das spezifische Element an alle und verschiedene. Die Schnittstelle abstrahiert dies und ignoriert die Implementierung vollständig und beschreibt nur die Erwartungen.
Leider machen viele Implementierungen das falsch und definieren die Schnittstelle dieser Art von Objekten, um einen direkten Zugriff auf das eigentliche Objekt zu ermöglichen. So etwas wie:
interface Container<T>
{
typedef ...T... TRef; //<somehow make TRef to be a reference or pointer to the memory location of T
TRef item(int index);
}
Das ist kaputt. Die Implementierungen von Container müssen die Kontrolle über ihre Interna explizit an denjenigen übergeben, der sie verwendet. Ich habe noch keine Sprache mit veränderlichem Wert gesehen, bei der dies in Ordnung ist (Sprachen mit Semantik mit unveränderlichem Wert sind per Definition in Bezug auf Datenkorruption in Ordnung, jedoch nicht unbedingt in Bezug auf Datenspionage).
Sie können den Get / Setter verbessern / korrigieren, indem Sie nur die Kopiersemantik oder einen Proxy verwenden:
interface Proxy<T>
{
operator T(); //<returns a copy
... operator ->(); //<permits a function call to be forwarded to an element
Proxy<T> operator=(T); //< permits the specific element to be replaced/assigned by another T.
}
interface Container<T>
{
Proxy<T> item(int index);
T item(int index); //<When T is a copy of the original value.
void item(int index, T new_value); //<where new_value is used to replace the old value
}
Möglicherweise könnte eine Rogue-Funktion hier noch Chaos anrichten (mit genügend Aufwand sind die meisten Dinge möglich), aber die Kopiersemantik und / oder der Proxy verringern die Wahrscheinlichkeit für eine Reihe von Fehlern.
- Überlauf
- Unterlauf
- Interaktionen mit dem Unterelement sind typüberprüft / typüberprüfbar (in Typ-Lose-Sprachen ist dies ein Segen)
- Das tatsächliche Element kann speicherresident sein oder nicht.
Private Getter / Setter
Dies ist die letzte Bastion von Gettern und Setzern, die direkt an dem Typ arbeiten. Tatsächlich würde ich diese Getter und Setter nicht einmal als Accessoren und Manipulatoren bezeichnen.
In diesem Zusammenhang erfordert die Manipulation eines bestimmten Teils der Datenstruktur manchmal immer / fast immer / im Allgemeinen eine bestimmte Buchführung. Sagen wir, wenn Sie das Stammverzeichnis eines Baums aktualisieren, muss der Look-Aside-Cache gelöscht werden, oder wenn Sie auf das externe Datenelement zugreifen, muss eine Sperre erlangt / aufgehoben werden. In diesen Fällen ist es sinnvoll, den DRY-Principal anzuwenden und diese Aktionen zusammenzufassen.
Im privaten Kontext ist es für die anderen Funktionen in der Familie immer noch möglich, diese "Get-and-Setter" zu umgehen und die Datenstruktur zu manipulieren. Deshalb betrachte ich sie eher als Accessoren und Manipulatoren. Sie können direkt auf die Daten zugreifen oder sich auf ein anderes Familienmitglied verlassen, um diesen Teil richtig zu machen.
Geschützte Getter / Setter
In einem geschützten Kontext unterscheidet es sich nicht sonderlich von einem öffentlichen Kontext. Fremd möglicherweise Rogue-Funktionen wollen Zugriff auf die Datenstruktur. Also nein, wenn sie existieren, funktionieren sie wie öffentliche Getter / Setter.
this->variable = x + 5
eineUpdateStatistics
Funktion im Setter ausführen oder aufrufen , was in diesen Fällenclassinstancea->variable = 5
zu Problemen führt.