Kapselung hat einen Zweck, kann aber auch missbraucht oder missbraucht werden.
Betrachten Sie etwas wie die Android-API, die Klassen mit Dutzenden (wenn nicht Hunderten) von Feldern enthält. Das Anzeigen dieser Felder durch den Benutzer der API erschwert das Navigieren und Verwenden und gibt dem Benutzer die falsche Vorstellung, dass er mit den Feldern, die möglicherweise in Konflikt mit der beabsichtigten Verwendung stehen, alles Mögliche tun kann. Daher ist die Kapselung in diesem Sinne großartig, um Wartbarkeit, Benutzerfreundlichkeit und Lesbarkeit zu gewährleisten und verrückte Fehler zu vermeiden.
Andererseits können auch POD- oder einfache alte Datentypen wie eine Struktur aus C / C ++, in der alle Felder öffentlich sind, nützlich sein. Es ist nur eine Möglichkeit, das "Encapsulation-Muster" beizubehalten, wenn unbrauchbare Get- / Setter vorhanden sind, wie sie durch die @ data-Annotation in Lombok generiert wurden. Einer der wenigen Gründe, warum wir in Java "nutzlose" Getter / Setter machen, ist, dass die Methoden einen Vertrag liefern .
In Java können Sie keine Felder in einer Schnittstelle haben. Daher verwenden Sie Getter und Setter, um eine gemeinsame Eigenschaft anzugeben, über die alle Implementierer dieser Schnittstelle verfügen. In neueren Sprachen wie Kotlin oder C # sehen wir das Konzept von Eigenschaften als Felder, für die Sie Setter und Getter deklarieren können. Am Ende sind nutzlose Getter / Setter eher ein Erbe, mit dem Java leben muss, es sei denn, Oracle fügt ihm Eigenschaften hinzu. Kotlin zum Beispiel, eine andere von JetBrains entwickelte JVM-Sprache, verfügt über Datenklassen, die im Grunde das tun, was die @ data-Annotation in Lombok tut.
Auch hier einige Beispiele:
class DataClass
{
private int data;
public int getData() { return data; }
public void setData(int data) { this.data = data; }
}
Dies ist ein schlechter Fall von Verkapselung. Der Getter und Setter sind effektiv nutzlos. Die Kapselung wird meistens verwendet, da dies der Standard in Sprachen wie Java ist. Hilft eigentlich nicht, außer die Konsistenz über die Codebasis aufrechtzuerhalten.
class DataClass implements IDataInterface
{
private int data;
@Override public int getData() { return data; }
@Override public void setData(int data) { this.data = data; }
}
Dies ist ein gutes Beispiel für die Verkapselung. Die Kapselung wird verwendet, um einen Vertrag durchzusetzen, in diesem Fall IDataInterface. Der Zweck der Kapselung in diesem Beispiel besteht darin, den Konsumenten dieser Klasse zu veranlassen, die von der Schnittstelle bereitgestellten Methoden zu verwenden. Obwohl Getter und Setter nichts Besonderes tun, haben wir jetzt ein gemeinsames Merkmal zwischen DataClass und anderen Implementierern von IDataInterface definiert. Somit kann ich eine Methode wie diese haben:
void doSomethingWithData(IDataInterface data) { data.setData(...); }
Nun, wenn ich über Kapselung spreche, denke ich, dass es wichtig ist, auch das Syntaxproblem anzugehen. Ich sehe oft Leute, die sich über die Syntax beschweren, die erforderlich ist, um die Kapselung zu erzwingen, anstatt die Kapselung selbst. Ein Beispiel, das mir einfällt, ist Casey Muratori (Sie können seine Schimpfe hier sehen ).
Angenommen, Sie haben eine Spielerklasse, die gekapselt ist und deren Position um 1 Einheit verschoben werden soll. Der Code würde so aussehen:
player.setPosX(player.getPosX() + 1);
Ohne Kapselung würde es so aussehen:
player.posX++;
Hier argumentiert er, dass Verkapselungen zu viel mehr Typisierung ohne zusätzlichen Nutzen führen und dies in vielen Fällen wahr sein kann, aber etwas beachten. Das Argument ist gegen die Syntax und nicht gegen die Kapselung. Sogar in Sprachen wie C, denen das Konzept der Kapselung fehlt, werden Sie häufig Variablen in Strukturen sehen, die mit '_' oder 'my' oder was auch immer versehen sind, um anzuzeigen, dass sie vom Verbraucher der API nicht verwendet werden sollten, als ob sie es wären Privatgelände.
Die Tatsache, dass es sich um eine Kapselung handelt, kann dazu beitragen, dass Code viel einfacher zu warten und zu verwenden ist. Betrachten Sie diese Klasse:
class VerticalList implements ...
{
private int posX;
private int posY;
... //other members
public void setPosition(int posX, int posY)
{
//change position and move all the objects in the list as well
}
}
Wenn die Variablen in diesem Beispiel öffentlich wären, wäre ein Verbraucher dieser API verwirrt, wann er posX und posY und wann setPosition () verwenden soll. Durch das Ausblenden dieser Details helfen Sie dem Verbraucher, Ihre API auf intuitive Weise besser zu nutzen.
Die Syntax ist jedoch in vielen Sprachen eine Einschränkung. Neuere Sprachen bieten jedoch Eigenschaften, die uns die nette Syntax von Veröffentlichungsmitgliedern und die Vorteile der Kapselung bieten. Sie finden Eigenschaften in C #, Kotlin, auch in C ++, wenn Sie MSVC verwenden. Hier ist ein Beispiel in Kotlin.
Klasse VerticalList: ... {var posX: Int set (x) {field = x; ...} var posY: Int set (y) {field = y; ...}}
Hier haben wir dasselbe wie im Java-Beispiel erreicht, aber wir können posX und posY so verwenden, als wären sie öffentliche Variablen. Wenn ich aber versuche, ihren Wert zu ändern, wird der Body des Settlers set () ausgeführt.
In Kotlin wäre dies beispielsweise das Äquivalent einer Java Bean mit Gettern, Setzern, Hashcode, Equals und ToString:
data class DataClass(var data: Int)
Beachten Sie, wie diese Syntax es uns ermöglicht, eine Java-Bean in einer Zeile auszuführen. Sie haben das Problem, das eine Sprache wie Java beim Implementieren der Kapselung hat, richtig bemerkt, aber das ist der Fehler von Java, nicht der Kapselung selbst.
Sie sagten, dass Sie Lomboks @Data verwenden, um Getter und Setter zu generieren. Beachten Sie den Namen @Data. Es ist hauptsächlich für Datenklassen gedacht, die nur Daten speichern und serialisiert und deserialisiert werden sollen. Stellen Sie sich so etwas wie eine Sicherungsdatei aus einem Spiel vor. In anderen Szenarien, wie bei einem UI-Element, möchten Sie jedoch unbedingt Setter, da das Ändern des Werts einer Variablen möglicherweise nicht ausreicht, um das erwartete Verhalten zu erzielen.
"It will create getters, setters and setting constructors for all private fields."
- Die Art und Weise Sie dieses Tool beschreiben, klingt es wie es ist Verkapselung aufrechterhalten wird . (Zumindest im Sinne eines losen, automatisierten, etwas anämischen Modells.) Also, was genau ist das Problem?