Ist die Kapselung immer noch einer der Elefanten, auf denen OOP steht?

97

Die Kapselung sagt mir, dass ich alle oder fast alle Felder privat machen und diese durch Getter / Setter verfügbar machen soll. Aber jetzt erscheinen Bibliotheken wie Lombok , die es uns ermöglichen, alle privaten Felder durch eine kurze Annotation freizugeben @Data. Es werden Getter, Setter und Setting-Konstruktoren für alle privaten Bereiche erstellt.

Könnte mir jemand erklären, was es heißt, alle Bereiche als privat zu verstecken und sie anschließend durch eine zusätzliche Technologie freizulegen? Warum verwenden wir dann einfach nicht nur öffentliche Felder? Ich habe das Gefühl, dass wir einen langen und schweren Weg zurückgelegt haben, nur um zum Ausgangspunkt zurückzukehren.

Ja, es gibt andere Technologien, die mit Gettern und Setzern arbeiten. Und wir können sie nicht durch einfache öffentliche Felder verwenden. Aber diese Technologien erschienen nur , weil wir hatten diese zahlreichen Eigenschaften - private Felder hinter öffentlichen Getter / Setter. Wenn wir nicht die Eigenschaften hätten, würden diese Technologien einen anderen Weg entwickeln und öffentliche Bereiche unterstützen. Und alles wäre einfach und wir hätten jetzt keinen Bedarf an Lombok.

Was ist der Sinn des Ganzen in diesem Zyklus? Und hat die Kapselung in der realen Programmierung jetzt wirklich einen Sinn?

Gangnus
quelle
19
"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?
David
78
Die Kapselung verbirgt Implementierungsinternale des Objekts hinter seinem öffentlichen Auftrag (normalerweise eine Schnittstelle). Getter und Setter machen genau das Gegenteil: Sie enthüllen die Interna des Objekts, sodass das Problem in Gettern / Settern und nicht in Kapseln besteht.
22
@VinceEmigh-Datenklassen haben keine Kapselung . Instanzen von ihnen sind Werte in genau dem Sinne, dass Primitive sind
Caleth
10
@VinceEmigh mit JavaBeans ist nicht OO , es ist prozedural . Dass die Literatur sie "Objekte" nennt, ist ein Fehler der Geschichte.
Caleth
28
Ich habe im Laufe der Jahre viel darüber nachgedacht. Ich denke, dies ist ein Fall, in dem sich die Absicht von OOP von der Implementierung unterschied. Nach dem Studium der SmallTalk es ist klar , was OOP die Verkapselung wurde bestimmt zu sein (dh jede Klasse wie ein unabhängiger Computer sein, mit Methoden wie das gemeinsame Protokoll), wenn auch aus Gründen bisher unbekannt zu mir, es auf jeden Fall in der Popularität gefangen hat , um diese Get- / Setter-Objekte, die keine konzeptionelle Kapselung bieten (sie verbergen nichts, verwalten nichts und haben keine andere Verantwortung als Daten) und dennoch Eigenschaften verwenden.
JRH

Antworten:

82

Wenn Sie alle Ihre Attribute mit Gettern / Setzern belichten, erhalten Sie nur eine Datenstruktur, die noch in C oder einer anderen prozeduralen Sprache verwendet wird. Es ist keine Verkapselung, und Lombok vereinfacht die Arbeit mit prozeduralem Code. Getter / Setter so schlecht wie normale öffentliche Felder. Es gibt wirklich keinen Unterschied.

Und Datenstruktur ist kein Objekt. Wenn Sie damit beginnen, ein Objekt aus dem Schreiben einer Schnittstelle zu erstellen, werden Sie der Schnittstelle niemals Getter / Setter hinzufügen. Das Offenlegen Ihrer Attribute führt zu Spaghetti-Prozedurcode, bei dem sich die Manipulation mit Daten außerhalb eines Objekts befindet und sich über die gesamte Codebasis verteilt. Jetzt beschäftigen Sie sich mit Daten und Manipulationen mit Daten, anstatt mit Objekten zu sprechen. Mit Gettern / Setzern verfügen Sie über eine datengesteuerte prozedurale Programmierung, bei der die Manipulation auf direkte und zwingende Weise erfolgt. Daten abrufen - etwas tun - Daten setzen.

Bei der OOP-Kapselung handelt es sich um einen Elefanten, wenn dies auf die richtige Weise erfolgt. Sie sollten Status- und Implementierungsdetails kapseln, damit das Objekt die volle Kontrolle darüber hat. Die Logik wird im Objekt fokussiert und nicht über die gesamte Codebasis verteilt. Und ja - die Kapselung ist bei der Programmierung immer noch unerlässlich, da der Code besser gewartet werden kann.

EDITS

Nachdem ich die Diskussionen gesehen habe, möchte ich einige Dinge hinzufügen:

  • Es spielt keine Rolle, wie viele Ihrer Attribute Sie durch Getter / Setter aussetzen und wie sorgfältig Sie dies tun. Wenn Sie selektiver vorgehen, wird Ihr Code nicht mit der Kapselung überlastet. Jedes Attribut, das Sie offenlegen, führt zu einer Prozedur, die mit diesen nackten Daten in einer zwingenden prozeduralen Weise arbeitet. Sie werden Ihren Code nur langsamer verbreiten, weil Sie selektiver sind. Das ändert nichts am Kern.
  • Ja, innerhalb des Systems erhalten Sie nackte Daten von anderen Systemen oder Datenbanken. Diese Daten sind jedoch nur ein weiterer Einkapselungspunkt.
  • Objekte sollten zuverlässig sein . Die ganze Idee von Objekten ist verantwortlich, damit Sie keine Befehle erteilen müssen , die direkt und zwingend sind. Stattdessen bitten Sie ein Objekt, das zu tun, was es im Vertrag gut macht. Sie delegieren den handelnden Teil sicher an das Objekt. Objekte kapseln Status- und Implementierungsdetails.

Wenn wir also zu der Frage zurückkehren, warum wir das tun sollten. Betrachten Sie dieses einfache Beispiel:

public class Document {
    private String title;

    public String getTitle() {
        return title;
    }
}

public class SomeDocumentServiceOrHandler {

    public void printDocument(Document document) {
        System.out.println("Title is " + document.getTitle());
    }
}

Hier haben wir ein Dokument, das interne Details durch Getter verfügbar macht, und einen externen Prozedurcode in printDocumentFunktion, der mit dem außerhalb des Objekts arbeitet. Warum ist das so schlimm? Denn jetzt haben Sie nur C-Style-Code. Ja, es ist strukturiert, aber was ist wirklich der Unterschied? Sie können C-Funktionen in verschiedenen Dateien und mit Namen strukturieren. Und genau das tun diese sogenannten Schichten. Die Serviceklasse besteht nur aus einer Reihe von Prozeduren, die mit Daten arbeiten. Dieser Code ist weniger wartbar und hat viele Nachteile.

public interface Printable {
    void print();
}

public final class PrintableDocument implements Printable {
    private final String title;

    public PrintableDocument(String title) {
        this.title = title;
    }

    @Override
    public void print() {
        System.out.println("Title is " + title);
    }
}

Vergleichen Sie mit diesem. Jetzt haben wir einen Vertrag und die Details zur Implementierung dieses Vertrags sind im Objekt verborgen . Jetzt können Sie diese Klasse wirklich testen und diese Klasse kapselt einige Daten. Wie es mit diesen Daten funktioniert, ist ein Gegenstand. Um jetzt mit dem Objekt zu sprechen , müssen Sie ihn bitten , sich selbst zu drucken. Das ist Verkapselung und das ist ein Objekt. Mit OOP erhalten Sie die volle Leistungsfähigkeit der Abhängigkeitsinjektion, des Verspottens, des Testens, der Einzelverantwortung und vieler Vorteile.

Izbassar Tolegen
quelle
Kommentare sind nicht für eine längere Diskussion gedacht. Diese Unterhaltung wurde in den Chat verschoben .
maple_shaft
1
"Getter / Setter so schlecht wie normale öffentliche Felder" - das stimmt zumindest in vielen Sprachen nicht. Normalerweise können Sie den normalen Feldzugriff nicht außer Kraft setzen, aber Getter / Setter überschreiben. Das gibt den Kinderklassen Vielseitigkeit. Es macht es auch einfach, das Verhalten von Klassen zu ändern. Sie können beispielsweise mit einem Getter / Setter beginnen, der durch ein privates Feld unterstützt wird, und später in ein anderes Feld wechseln oder den Wert aus anderen Werten berechnen. Beides ist mit einfachen Feldern nicht möglich. Einige Sprachen erlauben Feldern, was im Wesentlichen automatische Getter und Setter ist, aber Java ist keine solche Sprache.
Kat
Ähm, immer noch nicht überzeugt. Ihr Beispiel ist in Ordnung, aber bezeichnen Sie einfach einen anderen Codierungsstil, nicht den "richtigen" - der nicht existiert. Denken Sie daran, dass die meisten Sprachen heutzutage multiparadig sind und nur selten rein oder rein prozedural sind. Halte dich so fest an "pure OO-Konzepte". Als Beispiel - Sie können DI in Ihrem Beispiel nicht verwenden, weil Sie die Drucklogik mit einer Unterklasse gekoppelt haben. Das Drucken sollte nicht Teil eines Dokuments sein - ein printableDocument würde seinen druckbaren Teil (über einen Getter) einem PrinterService aussetzen.
T. Sar
Ein richtiger OO-Ansatz für diesen Fall würde, wenn wir einen "Pure OO" -Ansatz wählen , etwas verwenden, das eine abstrakte PrinterService-Klasse implementiert , die über eine Nachricht anfordert, was zum Teufel gedruckt werden soll - mit einer Art GetPrintablePart. Das, was den PrinterService implementiert, kann jede Art von Drucker sein - Drucken in ein PDF, auf den Bildschirm, in den TXT ... Ihre Lösung macht es unmöglich, die Drucklogik gegen etwas anderes auszutauschen, was zu einer besseren Kopplung und einer geringeren Wartung führt als dein "falsches" Beispiel.
T. Sar
Die Quintessenz: Getter und Setter sind nicht böse oder brechen OOP - Leute, die nicht verstehen, wie man sie benutzt, sind. Ihr Beispielfall ist ein Lehrbuchbeispiel für Menschen, denen der ganze Punkt fehlt, wie DI funktioniert. Das Beispiel, das Sie "korrigiert" haben, war bereits DI-aktiviert, entkoppelt, konnte leicht verspottet werden ... Wirklich - erinnern Sie sich an die ganze Sache "Komposition vor Vererbung bevorzugen"? Eine der OO-Säulen? Sie haben es gerade durch das Fenster geworfen, als Sie den Code überarbeitet haben. Dies würde keine ernsthafte Codeüberprüfung nach sich ziehen.
T. Sar
76

Könnte mir jemand erklären, wie es ist, alle Bereiche als privat zu verstecken und sie anschließend durch eine zusätzliche Technologie freizulegen? Warum benutzen wir dann nicht einfach nur öffentliche Felder?

Der Sinn ist, dass Sie das nicht tun sollen .

Kapselung bedeutet, dass Sie nur die Felder verfügbar machen, auf die andere Klassen zugreifen müssen, und dass Sie sehr selektiv und vorsichtig damit sind.

Geben Sie nicht standardmäßig alle Felder Getter und Setter an!

Das widerspricht vollkommen dem Geist der JavaBeans-Spezifikation, aus der ironischerweise das Konzept der öffentlichen Getter und Setter stammt.

Wenn Sie sich jedoch die Spezifikation ansehen, werden Sie feststellen, dass die Erstellung von Gettern und Setzern sehr selektiv sein sollte und dass es sich um schreibgeschützte Eigenschaften (kein Setter) und schreibgeschützte Eigenschaften (kein Setter) handelt getter).

Ein weiterer Faktor ist, dass Getter und Setter nicht unbedingt einen einfachen Zugang zum privaten Bereich darstellen . Ein Getter kann den Wert, den er zurückgibt, auf eine beliebig komplexe Weise berechnen, möglicherweise im Cache speichern. Ein Setter kann den Wert validieren oder Listener benachrichtigen.

Sie sind also hier: Kapselung bedeutet, dass Sie nur die Funktionen verfügbar machen, die Sie tatsächlich benötigen. Aber wenn Sie nicht darüber nachdenken, was Sie herausstellen müssen, und einfach alles aufdecken, indem Sie einer syntaktischen Transformation folgen, dann ist das natürlich keine wirkliche Verkapselung.

Michael Borgwardt
quelle
20
"[G] etter und Setter sind nicht unbedingt einfacher Zugang" - ich denke, das ist ein zentraler Punkt. Wenn Ihr Code über den Namen auf ein Feld zugreift, können Sie das Verhalten später nicht mehr ändern. Wenn Sie obj.get () verwenden, können Sie.
Dan Ambrogio
4
@jrh: Ich habe noch nie gehört, dass es sich bei Java um eine schlechte Praxis handelt, und sie ist ziemlich verbreitet.
Michael Borgwardt
2
@MichaelBorgwardt Interessant; Ein bisschen abseits des Themas, aber ich habe mich immer gefragt, warum Microsoft davon abgeraten hat, dies für C # zu tun. Ich vermute, diese Richtlinien implizieren, dass in C # der einzige Zweck, für den Setter verwendet werden können, die Konvertierung eines angegebenen Werts in einen anderen intern verwendeten Wert ist, auf eine Weise, die nicht fehlschlagen kann oder einen ungültigen Wert hat (z. B. eine PositionInches-Eigenschaft und eine PositionCentimeters-Eigenschaft) wo wandelt es sich intern automatisch in mm um)? Kein gutes Beispiel, aber es ist das Beste, was ich mir im Moment vorstellen kann.
JRH
2
@jrh diese Quelle sagt, es nicht für Getter zu tun, im Gegensatz zu Setter.
Captain Man
3
@Gangnus und du siehst wirklich, dass jemand eine Logik im Getter / Setter versteckt? Wir sind so daran gewöhnt, dass getFieldName()es für uns ein automatischer Vertrag wird. Wir werden dahinter kein komplexes Verhalten erwarten. In den meisten Fällen wird die Verkapselung gerade abgebrochen.
Izbassar Tolegen
17

Ich denke, der Kern der Sache erklärt sich aus Ihrem Kommentar:

Ich stimme Ihrem Gedanken voll und ganz zu. Aber irgendwo müssen wir Objekte mit Daten laden. Zum Beispiel aus XML. Und aktuelle Plattformen, die dies unterstützen, tun dies durch Getter / Setter, wodurch die Qualität unseres Codes und unserer Denkweise beeinträchtigt wird. Lombok ist an sich nicht schlecht, aber seine Existenz zeigt, dass wir etwas Schlechtes haben.

Das Problem, das Sie haben, ist, dass Sie das Persistenzdatenmodell mit dem aktiven Datenmodell mischen.

Eine Anwendung verfügt im Allgemeinen über mehrere Datenmodelle:

  • ein Datenmodell, um mit der Datenbank zu sprechen,
  • ein Datenmodell zum Lesen der Konfigurationsdatei,
  • ein Datenmodell, um mit einer anderen Anwendung zu sprechen,
  • ...

über dem Datenmodell, das es tatsächlich verwendet, um seine Berechnungen durchzuführen.

Im Allgemeinen sollten die für die Kommunikation mit der Außenseite verwendeten Datenmodelle isoliert und unabhängig von dem inneren Datenmodell (Business Object Model, BOM) sein, für das Berechnungen durchgeführt werden:

  • unabhängig : damit Sie Eigenschaften zur Stückliste hinzufügen / entfernen können, wie es Ihren Anforderungen entspricht, ohne alle Clients / Server ändern zu müssen, ...
  • isoliert : Damit alle Berechnungen in der Stückliste ausgeführt werden, in der sich Invarianten befinden, und das Wechseln von einem Dienst zu einem anderen oder das Aktualisieren eines Dienstes keine Welligkeit in der gesamten Codebasis verursacht.

In diesem Szenario ist es völlig in Ordnung, wenn für die in den Kommunikationsebenen verwendeten Objekte alle Elemente öffentlich sind oder von Gettern / Setzern verfügbar gemacht werden. Das sind einfache Objekte ohne jegliche Invariante.

Auf der anderen Seite sollte Ihre Stückliste Invarianten enthalten, was im Allgemeinen viele Setter ausschließt (Getter haben keinen Einfluss auf Invarianten, obwohl sie die Verkapselung bis zu einem gewissen Grad reduzieren).

Matthieu M.
quelle
3
Ich würde keine Getter und Setter für Kommunikationsobjekte erstellen. Ich würde nur die Felder öffentlich machen. Warum mehr Arbeit schaffen als sinnvoll ist?
immibis
3
@immibis: Ich stimme im Allgemeinen zu, jedoch erfordern einige Frameworks, wie im OP vermerkt, Getter / Setter. In diesem Fall müssen Sie nur die Vorgaben einhalten. Beachten Sie, dass das OP eine Bibliothek verwendet, die sie automatisch mit der Anwendung eines einzelnen Attributs erstellt, so dass es für ihn wenig Arbeit ist.
Matthieu M.
1
@Gangnus: Die Idee hier ist, dass die Getter / Setter auf die Grenzschicht (I / O) beschränkt sind, wo es normal ist, schmutzig zu werden, aber der Rest der Anwendung (reiner Code) ist nicht kontaminiert und bleibt elegant.
Matthieu M.
2
@kubanczyk: Die offizielle Definition ist nach meinem Kenntnisstand Business Object Model. Es entspricht hier dem, was ich "inneres Datenmodell" nannte, dem Datenmodell, auf dem Sie Logik im "reinen" Kern Ihrer Anwendung ausführen.
Matthieu M.
1
Dies ist der pragmatische Ansatz. Wir leben in einer hybriden Welt. Wenn die externen Bibliotheken wissen könnten, welche Daten an die Stücklistenkonstruktoren zu übergeben sind, hätten wir nur Stücklisten.
Adrian Iftode
12

Folgendes berücksichtigen..

Sie haben eine UserKlasse mit einer Eigenschaft int age:

class User {
    int age;
}

Sie möchten dies so skalieren, dass a Userein Geburtsdatum hat und nicht nur ein Alter. Getter verwenden:

class User {
    private int age;

    public int getAge() {
        return age;
    }
}

Wir können das int ageFeld gegen ein komplexeres austauschen LocalDate dateOfBirth:

class User {
    private LocalDate dateOfBirth;

    public int getAge() {
        LocalDate now = LocalDate.now();
        int year = ...; // calculate using dateOfBirth and now
        return year;
    }

    // other behaviors can now make use of dateOfBirth
}

Keine Vertragsverstöße, kein Codefehler. Nur die interne Darstellung muss vergrößert werden, um sich auf komplexere Verhaltensweisen vorzubereiten.

Das Feld selbst ist eingekapselt.


Nun, um die Bedenken auszuräumen ..

Die @DataAnnotation von Lombok ähnelt den Datenklassen von Kotlin .

Nicht alle Klassen repräsentieren Verhaltensobjekte. Die Unterbrechung der Kapselung hängt von Ihrer Verwendung der Funktion ab. Sie sollten nicht alle Ihre Felder über Getter aussetzen .

Die Kapselung wird verwendet, um die Werte oder den Status eines strukturierten Datenobjekts innerhalb einer Klasse auszublenden

In einem allgemeineren Sinne ist die Kapselung das Verbergen von Informationen. Wenn Sie missbrauchen @Data, ist es leicht anzunehmen, dass Sie wahrscheinlich die Kapselung brechen. Aber das heißt nicht, dass es keinen Zweck hat. JavaBeans zum Beispiel werden von manchen missbilligt. Es wird jedoch in großem Umfang in der Unternehmensentwicklung eingesetzt.

Würden Sie zu dem Schluss kommen, dass die Unternehmensentwicklung aufgrund der Verwendung von Bohnen schlecht ist? Natürlich nicht! Die Anforderungen unterscheiden sich von denen der Standardentwicklung. Können Bohnen missbraucht werden? Na sicher! Sie werden die ganze Zeit missbraucht!

Lombok unterstützt auch @Getterund @Setterunabhängig - verwenden Sie, was Ihre Anforderungen erfordern.

Vince Emigh
quelle
2
Dies ist in keinem Zusammenhang mit der slapping @DataAnmerkung auf eine Art, die durch Design unencapsulates alle Felder
Caleth
1
Nein, die Felder werden nicht ausgeblendet . Weil alles mitkommen kann undsetAge(xyz)
Caleth
9
@Caleth Die Felder sind ausgeblendet. Sie tun so, als ob Setter keine Vor- und Nachbedingungen haben könnten. Dies ist ein sehr häufiger Anwendungsfall für Setter. Sie tun so, als ob eine field == -Eigenschaft, die auch in Sprachen wie C # nicht zutrifft, da beide unterstützt werden (wie in C #). Das Feld kann in eine andere Klasse verschoben werden, es kann für eine komplexere Darstellung ausgetauscht werden ...
Vince Emigh
1
Und was ich sage, ist, dass das nicht wichtig ist
Caleth
2
@ Caleth Wie ist es nicht wichtig? Das ergibt keinen Sinn. Sie sagen, dass die Einkapselung nicht gilt, weil Sie der Meinung sind, dass sie in dieser Situation nicht wichtig ist, obwohl sie per Definition gilt und Anwendungsfälle aufweist?
Vince Emigh
11

Die Kapselung sagt mir, dass ich alle oder fast alle Felder privat machen und diese durch Getter / Setter verfügbar machen soll.

So wird Kapselung in der objektorientierten Programmierung nicht definiert. Einkapselung bedeutet, dass jedes Objekt wie eine Kapsel sein sollte, deren äußere Hülle (die öffentliche API) den Zugang zu seinem Inneren (den privaten Methoden und Feldern) schützt und reguliert und es vor dem Blick verbirgt. Durch das Ausblenden von Interna können sich Anrufer nicht auf Interna verlassen, sodass Interna geändert werden können, ohne dass die Anrufer geändert (oder sogar neu kompiliert) werden. Durch die Kapselung kann jedes Objekt seine eigenen Invarianten erzwingen, indem nur Aufrufern sichere Operationen zur Verfügung gestellt werden.

Die Kapselung ist daher ein Sonderfall des Versteckens von Informationen, bei dem jedes Objekt seine Interna verbirgt und seine Invarianten erzwingt.

Das Generieren von Get- und Setter-Werten für alle Felder ist eine ziemlich schwache Form der Kapselung, da die Struktur der internen Daten nicht verborgen ist und Invarianten nicht erzwungen werden können. Dies hat den Vorteil, dass Sie die Art und Weise ändern können, in der die Daten intern gespeichert werden (solange Sie in die alte Struktur konvertieren können), ohne jedoch die Aufrufer ändern (oder sogar neu kompilieren zu müssen).

Könnte mir jemand erklären, was es heißt, alle Bereiche als privat zu verstecken und sie anschließend durch eine zusätzliche Technologie freizulegen? Warum verwenden wir dann einfach nicht nur öffentliche Felder? Ich habe das Gefühl, dass wir einen langen und schweren Weg zurückgelegt haben, nur um zum Ausgangspunkt zurückzukehren.

Dies ist zum Teil auf einen historischen Unfall zurückzuführen. Auffällig ist, dass in Java Methodenaufrufausdrücke und Feldzugriffsausdrücke an der Aufrufstelle syntaktisch unterschiedlich sind, dh das Ersetzen eines Feldzugriffs durch einen Getter- oder Setter-Aufruf unterbricht die API einer Klasse. Wenn Sie daher möglicherweise einen Accessor benötigen, müssen Sie jetzt einen schreiben oder die API unterbrechen. Dieses Fehlen der Unterstützung von Eigenschaften auf Sprachebene steht in scharfem Kontrast zu anderen modernen Sprachen, insbesondere C # und EcmaScript .

Strike 2 ist, dass die JavaBeans- Spezifikation Eigenschaften als Getter / Setter definiert hat, Felder waren keine Eigenschaften. Infolgedessen unterstützten die meisten frühen Enterprise-Frameworks Getter / Setter, jedoch keine Felder. Das ist längst Vergangenheit ( Java Persistence API (JPA) , Bean Validation , Java-Architektur für XML-Bindung (JAXB) , Jackson)Inzwischen sind alle Support-Bereiche in Ordnung), aber die alten Tutorials und Bücher verweilen weiter und nicht jeder weiß, dass sich die Dinge geändert haben. In einigen Fällen kann das Fehlen der Unterstützung von Eigenschaften auf Sprachebene immer noch problematisch sein (z. B. weil das verzögerte Laden einzelner Entitäten durch JPA nicht ausgelöst wird, wenn öffentliche Felder gelesen werden), aber die meisten öffentlichen Felder funktionieren einwandfrei. Mein Unternehmen schreibt also alle DTOs für ihre REST-APIs mit öffentlichen Feldern (es wird schließlich nicht mehr öffentlich, als über das Internet übertragen wird :-).

Das heißt, Lomboks @Dataerzeugt mehr als nur Getter / Setter: Sie erzeugt toString()auch hashCode()und equals(Object), was sehr wertvoll sein kann.

Und hat die Kapselung in der realen Programmierung jetzt wirklich einen Sinn?

Die Verkapselung kann von unschätzbarem Wert oder völlig unbrauchbar sein, sie hängt vom zu verkapselnden Objekt ab. Je komplexer die Logik innerhalb der Klasse ist, desto größer ist im Allgemeinen der Vorteil der Kapselung.

Automatisch generierte Get- und Setter-Funktionen für jedes Feld werden im Allgemeinen zu häufig verwendet. Sie können jedoch hilfreich sein, um mit älteren Frameworks zu arbeiten oder um die gelegentliche Framework-Funktion zu verwenden, die für Felder nicht unterstützt wird.

Die Kapselung kann mit Gettern und Befehlsmethoden erreicht werden. Setter sind normalerweise nicht geeignet, da erwartet wird, dass sie nur ein einziges Feld ändern, während bei der Pflege von Invarianten möglicherweise mehrere Felder gleichzeitig geändert werden müssen.

Zusammenfassung

Getter / Setter bieten eine eher schlechte Kapselung.

Die Prävalenz von Gettern / Setzern in Java beruht auf dem Mangel an Sprachniveauunterstützung für Eigenschaften und fragwürdigen Entwurfswahlen in seinem historischen Komponentenmodell, die in vielen Lehrmaterialien und den von ihnen unterrichteten Programmierern verankert sind.

Andere objektorientierte Sprachen wie EcmaScript unterstützen Eigenschaften auf Sprachebene, sodass Getter eingeführt werden können, ohne die API zu beschädigen. In solchen Sprachen können Getter eingeführt werden, wenn Sie sie tatsächlich benötigen, und nicht im Voraus, nur für den Fall, dass Sie sie einmal brauchen. Dies sorgt für ein weitaus angenehmeres Programmiererlebnis.

Meriton
quelle
1
erzwingt seine Invarianten? Ist es englisch Könnten Sie es bitte einem Nicht-Engländer erklären? Seien Sie nicht zu kompliziert - einige Leute hier sprechen nicht gut genug Englisch.
Gangnus
Ich mag deine Basis, ich mag deine Gedanken (+1), aber nicht das praktische Ergebnis. Ich würde lieber privates Feld und eine spezielle Bibliothek verwenden, um Daten für sie durch Reflektion zu laden. Ich verstehe nur nicht, warum Sie Ihre Antwort als Argument gegen mich setzen?
Gangnus
@Gangnus Eine Invariante ist eine logische Bedingung, die sich nicht ändert. Viele Funktionen haben Regeln, die wahr sein müssen, bevor und nachdem sie ausgeführt werden (Pre-Conditions und Post-Conditions genannt). Andernfalls liegt ein Fehler im Code vor. Zum Beispiel könnte eine Funktion verlangen, dass ihr Argument nicht null ist (eine Vorbedingung) und könnte eine Ausnahme auslösen, wenn ihr Argument null ist, weil dieser Fall ein Fehler im Code wäre (dh in der Informatik hat der Code einen Fehler gemacht) invariant).
Pharap
@Gangus: Ich bin mir nicht ganz sicher, wo diese Diskussion stattfindet. Lombok ist ein Annotation-Prozessor, dh ein Plugin für den Java-Compiler, der während der Kompilierung zusätzlichen Code ausgibt. Aber sicher, Sie können Lombok verwenden. Ich sage nur, dass öffentliche Felder in vielen Fällen genauso gut funktionieren und einfacher einzurichten sind (nicht alle Compiler erkennen Anmerkungsprozessoren automatisch ...).
Meriton
1
@Gangnus: Invarianten sind in Mathe und CS gleich, aber in CS gibt es eine zusätzliche Perspektive: Aus der Perspektive eines Objekts müssen Invarianten erzwungen und festgelegt werden. Aus der Sicht eines Aufrufers dieses Objekts sind Invarianten immer wahr.
Meriton
7

Ich habe mir diese Frage tatsächlich gestellt.

Es ist jedoch nicht ganz wahr. Die Prävalenz von Get- / Setter-IMO wird durch die Java-Bean-Spezifikation verursacht, die dies erfordert. Es ist also so ziemlich ein Merkmal der nicht objektorientierten Programmierung, sondern der bean-orientierten Programmierung, wenn Sie so wollen. Der Unterschied zwischen den beiden besteht in der Abstraktionsschicht, in der sie existieren. Beans sind eher eine Systemschnittstelle, dh auf einer höheren Ebene. Sie abstrahieren von der OO-Basisarbeit oder sind zumindest dazu gedacht - wie immer werden die Dinge zu oft genug zu weit getrieben.

Ich würde sagen, es ist etwas bedauerlich, dass diese Bean-Sache, die in der Java-Programmierung allgegenwärtig ist, nicht mit einer entsprechenden Java-Sprachfunktion einhergeht - ich denke an so etwas wie das Properties-Konzept in C #. Für diejenigen, die sich dessen nicht bewusst sind, ist es ein Sprachkonstrukt, das so aussieht:

class MyClass {
    string MyProperty { get; set; }
}

Wie auch immer, die eigentliche Implementierung profitiert immer noch sehr stark von der Kapselung.

daniu
quelle
Python hat eine ähnliche Funktion, bei der Eigenschaften transparente Getter / Setter haben können. Ich bin mir sicher, dass es noch mehr Sprachen gibt, die ähnliche Funktionen haben.
JAB
1
"Die Prävalenz von Get / Setter IMO wird durch die Java Bean-Spezifikation verursacht, die dies erfordert." Ja, Sie haben Recht. Und wer hat gesagt, dass es erforderlich sein muss? Der Grund, bitte?
Gangnus
2
Der C # Weg ist absolut der gleiche wie dieser Lombok. Ich sehe keinen wirklichen Unterschied. Mehr praktische Bequemlichkeit, aber offensichtlich schlecht in der Konzeption.
Gangnus
1
@ Gangnis Die Spezifikation erfordert es. Warum? Schauen Sie sich meine Antwort an, um ein konkretes Beispiel dafür zu finden, warum Getter nicht mit dem Freilegen von Feldern identisch sind. Versuchen Sie, dasselbe auch ohne Getter zu erreichen.
Vince Emigh
@VinceEmigh GangnUs, bitte :-). (Gang-Walking, Nuss). Und was ist mit get / set nur dort, wo wir es speziell brauchen, und dem Massenladen in private Felder durch Reflektion in anderen Fällen?
Gangnus
6

Könnte mir jemand erklären, wie es ist, alle Bereiche als privat zu verstecken und sie anschließend durch eine zusätzliche Technologie freizulegen? Warum benutzen wir dann nicht einfach nur öffentliche Felder? Ich habe das Gefühl, dass wir einen langen und schweren Weg zurückgelegt haben, um zum Ausgangspunkt zurückzukehren.

Die einfache Antwort lautet hier: Sie haben absolut Recht. Getter und Setter eliminieren den größten Teil (aber nicht den gesamten) Wert der Verkapselung. Dies soll nicht heißen, dass Sie jedes Mal, wenn Sie eine get- und / oder set-Methode haben, die die Kapselung unterbricht, aber wenn Sie allen privaten Mitgliedern Ihrer Klasse blind Zugriffsmethoden hinzufügen, machen Sie es falsch.

Ja, es gibt andere Technologien, die mit Gettern und Setzern arbeiten. Und wir können sie nicht durch einfache öffentliche Felder verwenden. Aber diese Technologien erschienen nur, weil wir diese zahlreichen Eigenschaften hatten - private Felder hinter öffentlichen Gettern / Setzern. Wenn wir nicht die Eigenschaften hätten, würden diese Technologien einen anderen Weg entwickeln und öffentliche Bereiche unterstützen. Und alles wäre einfach und wir hätten jetzt keinen Bedarf an Lombok. Was ist der Sinn des Ganzen in diesem Zyklus? Und hat die Kapselung in der realen Programmierung jetzt wirklich einen Sinn?

Getter sind Setter, die in der Java-Programmierung allgegenwärtig sind, da das JavaBean-Konzept als Methode zur dynamischen Bindung der Funktionalität an vorgefertigten Code eingeführt wurde. Sie könnten zum Beispiel ein Formular in einem Applet haben (jemand erinnert sich daran?), Das Ihr Objekt inspiziert, alle Eigenschaften findet und die Felder als anzeigt. Dann kann die Benutzeroberfläche diese Eigenschaften basierend auf Benutzereingaben ändern. Sie als Entwickler kümmern sich dann nur darum, die Klasse zu schreiben und dort eine Validierung oder Geschäftslogik usw. abzulegen.

Bildbeschreibung hier eingeben

Beispielbohnen verwenden

Das ist an sich keine schreckliche Idee, aber ich war noch nie ein großer Fan des Ansatzes in Java. Es geht nur gegen den Strich. Verwenden Sie Python, Groovy usw. Etwas, das diesen Ansatz natürlicher unterstützt.

Das JavaBean-Ding ist außer Kontrolle geraten, weil es JOBOL erstellt hat, dh von Java geschriebene Entwickler, die OO nicht verstehen. Grundsätzlich wurden Objekte zu nichts weiter als zu Datenbeuteln, und die gesamte Logik wurde mit langen Methoden nach außen geschrieben. Weil es als normal angesehen wurde, galten Leute wie du und ich, die dies in Frage stellten, als verrückt. In letzter Zeit habe ich eine Veränderung erlebt und dies ist keine so starke Außenseiterposition

Die XML-Bindung ist eine harte Nuss. Dies ist wahrscheinlich kein gutes Schlachtfeld, um sich gegen JavaBeans zu behaupten. Wenn Sie diese JavaBeans erstellen müssen, versuchen Sie, sie aus dem tatsächlichen Code herauszuhalten. Behandeln Sie sie als Teil der Serialisierungsebene.

JimmyJames
quelle
2
Die konkretesten und klügsten Gedanken. +1. Aber warum nicht eine Gruppe von Klassen schreiben, von denen jede massiv private Felder von / zu einer externen Struktur lädt / speichert? JSON, XML, anderes Objekt. Es könnte mit POJOs oder vielleicht nur mit Feldern funktionieren, die durch eine Anmerkung dafür gekennzeichnet sind. Jetzt denke ich über diese Alternative nach.
Gangnus
@Gangnus Vermutlich bedeutet das viel zusätzlichen Code. Wenn Reflektion verwendet wird, muss nur ein Block Serialisierungscode geschrieben werden, und es kann jede Klasse serialisiert werden, die dem spezifischen Muster folgt. C # unterstützt dies durch das [Serializable]Attribut und seine Verwandten. Warum 101 spezifische Serialisierungsmethoden / -klassen schreiben, wenn Sie nur eine schreiben können, indem Sie Reflection einsetzen?
Pharap
@Pharap Es tut mir schrecklich leid, aber Ihr Kommentar ist zu kurz für mich. "Reflexion nutzen"? Und was ist das für ein Gefühl, so unterschiedliche Dinge in einer Klasse zu verstecken? Können Sie erklären, was Sie meinen?
Gangnus
@Gangnus Ich meine Reflexion , die Fähigkeit des Codes, seine eigene Struktur zu untersuchen. In Java (und anderen Sprachen) können Sie eine Liste aller Funktionen einer Klasse abrufen und diese dann verwenden, um die Daten aus der Klasse zu extrahieren, die für die Serialisierung in z. B. XML / JSON erforderlich sind. Reflexion wäre eine einmalige Aufgabe, anstatt ständig neue Methoden zu entwickeln, um Strukturen auf Klassenbasis zu speichern. Hier ist ein einfaches Java-Beispiel .
Pharap
@Pharap Das ist genau das, worüber ich gesprochen habe. Aber warum nennst du es "Leveraging"? Und die Alternative, die ich hier im ersten Kommentar erwähnt habe, bleibt bestehen - sollten (ungepackte) Felder spezielle Anmerkungen haben oder nicht?
Gangnus
5

Wie viel können wir ohne Getter erreichen? Ist es möglich, sie vollständig zu entfernen? Welche Probleme entstehen dadurch? Könnten wir das returnKeyword sogar verbieten ?

Es stellt sich heraus, dass Sie viel tun können, wenn Sie bereit sind, die Arbeit zu erledigen. Wie können dann Informationen aus diesem vollständig gekapselten Objekt herauskommen? Durch einen Mitarbeiter.

Anstatt sich von Code Fragen stellen zu lassen, sagen Sie, was zu tun ist. Wenn diese Dinge auch keine Dinge zurückgeben, müssen Sie nichts dagegen tun, was sie zurückgeben. Wenn Sie also überlegen, returnstattdessen nach einem Output Port Collaborator zu greifen, der alles tut, was noch zu tun ist.

Das hat Vorteile und Konsequenzen. Sie müssen über mehr nachdenken, als nur über das, was Sie zurückgegeben hätten. Sie müssen darüber nachdenken, wie Sie das als Nachricht an ein Objekt senden, das nicht danach gefragt hat. Es kann sein, dass Sie dasselbe Objekt herausgeben, das Sie zurückgegeben hätten, oder es reicht aus, einfach eine Methode aufzurufen. Das Denken ist mit Kosten verbunden.

Der Vorteil ist, dass Sie jetzt mit Blick auf die Benutzeroberfläche sprechen. Dies bedeutet, dass Sie den vollen Nutzen aus der Abstraktion ziehen können.

Es gibt Ihnen auch einen polymorphen Versand, denn während Sie wissen, was Sie sagen, müssen Sie nicht genau wissen, zu was Sie es sagen.

Sie denken vielleicht, dies bedeutet, dass Sie nur einen Weg durch einen Stapel von Schichten gehen müssen, aber es stellt sich heraus, dass Sie Polymorphismus verwenden können, um rückwärts zu gehen, ohne verrückte zyklische Abhängigkeiten zu erzeugen.

Es könnte so aussehen:

Geben Sie hier eine Bildbeschreibung ein

class Interactor implements InputPort {
    OutputPort out;
    int y = 0;

    Interactor(OutputPort out){
        this.out = out;
    }

    void accumulate(int x) {
        y = y + x;
        out.showAsImage(y);
    }
}

Wenn Sie so codieren können, ist die Verwendung von Gettern eine Wahl. Kein notwendiges Übel.

kandierte_orange
quelle
Ja, solche Getter / Setter haben großen Sinn. Aber verstehst du, dass es um etwas anderes geht? :-) Trotzdem +1.
Gangnus
1
@ Gangnus Vielen Dank. Es gibt eine Reihe von Dingen, über die Sie sprechen könnten. Wenn Sie sie wenigstens benennen möchten, könnte ich darauf eingehen.
candied_orange
5

Dies ist ein kontroverses Thema (wie Sie sehen können), da eine Reihe von Dogmen und Missverständnissen mit den angemessenen Bedenken in Bezug auf das Thema Getter und Setter verwechselt werden. Aber kurz gesagt, es ist nichts falsch daran @Dataund es bricht nicht die Kapselung.

Warum Getter und Setter anstelle von öffentlichen Feldern verwenden?

Weil Getter / Setter für die Kapselung sorgen. Wenn Sie einen Wert als öffentliches Feld verfügbar machen und später ändern, um den Wert im laufenden Betrieb zu berechnen, müssen Sie alle Clients ändern, die auf das Feld zugreifen. Das ist eindeutig schlecht. Es ist ein Implementierungsdetail, ob eine Eigenschaft eines Objekts in einem Feld gespeichert, im laufenden Betrieb generiert oder von einem anderen Ort abgerufen wird, sodass der Unterschied nicht für Clients sichtbar gemacht werden sollte. Getter / Setter Setter lösen dies, da sie die Implementierung verbergen.

Aber wenn der Getter / Setter nur ein zugrunde liegendes privates Feld widerspiegelt, ist es nicht genauso schlimm?

Nein! Der Punkt ist, dass die Kapselung es Ihnen ermöglicht , die Implementierung zu ändern, ohne die Clients zu beeinflussen. Ein Feld ist möglicherweise immer noch eine gute Methode zum Speichern von Werten, solange die Kunden es nicht wissen oder sich darum kümmern müssen.

Aber werden Getter / Setter nicht automatisch aus Feldern generiert, die die Kapselung durchbrechen?

Nein die Kapselung ist noch da! @DataAnmerkungen sind nur eine bequeme Möglichkeit, Getter / Setter-Paare zu schreiben, die ein zugrunde liegendes Feld verwenden. Für den Standpunkt eines Clients ist dies genau wie ein reguläres Get- / Setter-Paar. Wenn Sie sich entscheiden, die Implementierung neu zu schreiben, können Sie dies dennoch tun, ohne den Client zu beeinträchtigen. So erhalten Sie das Beste aus beiden Welten: Kapselung und kurze Syntax.

Aber manche sagen, Getter / Setter sind immer schlecht!

Es gibt eine separate Kontroverse, bei der einige der Ansicht sind, dass das Get- / Setter-Muster unabhängig von der zugrunde liegenden Implementierung immer schlecht ist . Die Idee ist , dass Sie nicht Werte von Objekten erhalten setzen oder sollten vielmehr jede Interaktion zwischen Objekten als Nachrichten modelliert werden soll , wenn ein Objekt fragt ein anderes Objekt , etwas zu tun. Dies ist meist ein Stück Dogma aus den Anfängen des objektorientierten Denkens. Es wird jetzt davon ausgegangen, dass für bestimmte Muster (z. B. Wertobjekte, Datenübertragungsobjekte) Getter / Setter durchaus geeignet sein können.

JacquesB
quelle
Die Einkapselung sollte uns Polymorphismus und Sicherheit ermöglichen. Gets / Sets erlauben die Tannen, sind aber stark gegen die Sekunde.
Gangnus
Wenn Sie sagen, dass ich irgendwo ein Dogma verwende, sagen Sie bitte, was aus meinem Text ein Dogma ist. Und vergiss nicht, dass ich einige Gedanken, die ich benutze, nicht mag oder mit denen ich nicht einverstanden bin. Ich benutze sie zur Demonstration eines Widerspruchs.
Gangnus
1
"GangNus" :-). Vor einer Woche habe ich an einem Projekt gearbeitet, das buchstäblich aus mehreren Ebenen besteht. Es ist absolut unsicher, und noch schlimmer, es unterstützt Menschen bei unsicherer Codierung. Ich bin froh, dass ich den Job schnell genug gewechselt habe, denn die Leute gewöhnen sich daran . Also, ich denke es nicht, ich sehe es .
Gangnus
2
@jrh: Ich weiß nicht, ob es eine formale Erklärung als solche gibt, aber C # unterstützt Eigenschaften (Getter / Setter mit einer schöneren Syntax) und anonyme Typen, die nur Eigenschaften und kein Verhalten aufweisen. Daher haben die Designer von C # bewusst von der Messaging-Metapher abgewichen und das Prinzip "Tell, Don't Ask" gewählt. Die Entwicklung von C # seit Version 2.0 scheint eher von funktionalen Sprachen als von traditioneller OO-Reinheit inspiriert zu sein.
JacquesB
1
@jrh: Ich vermute jetzt, aber ich denke, C # ist ziemlich inspiriert von funktionalen Sprachen, bei denen Daten und Operationen mehr voneinander getrennt sind. In verteilten Architekturen hat sich die Perspektive auch von nachrichtenorientiert (RPC, SOAP) zu datenorientiert (REST) ​​verschoben. Und Datenbanken, die Daten verfügbar machen und verarbeiten (keine "Black Box" -Objekte), haben sich durchgesetzt. Kurz gesagt, ich denke, der Fokus hat sich von Nachrichten zwischen Black Boxes auf exponierte Datenmodelle verlagert
JacquesB
3

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.

BananyaDev
quelle
Könnten Sie hier ein Beispiel für den Missbrauch der Kapselung anführen? Das könnte interessant sein. Ihr Beispiel ist eher gegen die fehlende Kapselung.
Gangnus
1
@Gangnus Ich habe ein paar Beispiele hinzugefügt. Unnötige Kapselung ist im Allgemeinen ein Missbrauch, und meiner Erfahrung nach versuchen API-Entwickler zu sehr, Sie zu zwingen, eine API auf eine bestimmte Art und Weise zu verwenden. Ich habe kein Beispiel dafür angeführt, weil ich kein leicht zu präsentierendes hatte. Wenn ich einen finde, werde ich ihn definitiv hinzufügen. Ich denke, die meisten Kommentare gegen die Kapselung sind eigentlich gegen die Syntax der Kapselung und nicht gegen die Kapselung selbst.
BananyaDev
Danke für die Bearbeitung. Ich fand jedoch einen kleinen Tippfehler: "publice" statt "public".
JRH
2

Die Kapselung gibt Ihnen Flexibilität . Durch die Trennung von Struktur und Schnittstelle können Sie die Struktur ändern, ohne die Schnittstelle zu ändern.

Wenn Sie beispielsweise feststellen, dass Sie eine Eigenschaft basierend auf anderen Feldern berechnen müssen, anstatt das zugrunde liegende Feld bei der Konstruktion zu initialisieren, können Sie einfach den Getter ändern. Wenn Sie das Feld direkt verfügbar gemacht hätten, müssten Sie stattdessen die Benutzeroberfläche ändern und an jeder Verwendungsstelle Änderungen vornehmen.

glennsl
quelle
Denken Sie daran, dass das Auslösen von Ausnahmen in Version 2 einer API, die nicht in Version 1 enthalten waren, die Benutzer Ihrer Bibliothek oder Klasse fürchterlich (und möglicherweise im Hintergrund!) Stören würde. Ich bin ein bisschen skeptisch, dass bei den meisten dieser Datenklassen größere Änderungen "hinter den Kulissen" vorgenommen werden könnten.
JRH
Entschuldigung, das ist keine Erklärung. Die Tatsache, dass die Verwendung von Feldern in Schnittstellen verboten ist, stammt aus der Idee der Kapselung. Und jetzt reden wir darüber. Sie stützen sich auf die diskutierten Fakten. (Beachten Sie, ich spreche nicht für oder gegen Ihre Gedanken, nur darüber, dass sie hier nicht als Argumente verwendet werden können)
Gangnus
@Gangnus Das Wort "Schnittstelle" hat eine Bedeutung außerhalb des Java-Schlüsselworts: dictionary.com/browse/interface
glennsl
@jrh Das ist ein ganz anderes Thema. Sie können es verwenden, um gegen die Einkapselung in bestimmten Kontexten zu argumentieren , aber es macht die Argumente dafür in keiner Weise ungültig.
Glennsl
1
Die alte Kapselung, ohne Masse zu bekommen / zu setzen, gab uns nicht nur Polymorphismus, sondern auch die Sicherheit. Und Massensätze bringen uns die schlechte Praxis bei.
Gangnus
2

Ich werde versuchen, den Problemraum der Kapselung und des Klassenentwurfs zu veranschaulichen und Ihre Frage am Ende zu beantworten.

Wie in anderen Antworten erwähnt, besteht der Zweck der Kapselung darin, interne Details eines Objekts hinter einer öffentlichen API zu verbergen, die als Vertrag dient. Es ist sicher, dass das Objekt seine Interna ändert, da es weiß, dass es nur über die öffentliche API aufgerufen wird.

Ob es sinnvoll ist, öffentliche Felder oder Get- / Setter, übergeordnete Transaktionsmethoden oder die Weitergabe von Nachrichten zu verwenden, hängt von der Art der zu modellierenden Domäne ab. In dem Buch Akka Concurrency (das ich empfehlen kann, auch wenn es etwas veraltet ist) finden Sie ein Beispiel, das dies veranschaulicht, das ich hier abkürzen werde.

Betrachten Sie eine Benutzerklasse:

public class User {
  private String first = "";
  private String last = "";

  public String getFirstName() {
    return this.first;
  }
  public void setFirstName(String s) {
    this.first = s;
  }

  public String getLastName() {
    return this.last;
  }
  public void setLastName(String s) {
    this.last = s;
  }
}

Dies funktioniert problemlos in einem Single-Thread-Kontext. Die Domain, die modelliert wird, ist der Name einer Person, und die Mechanik, wie dieser Name gespeichert wird, kann von den Setzern perfekt eingekapselt werden.

Stellen Sie sich jedoch vor, dies muss in einem Kontext mit mehreren Threads bereitgestellt werden. Angenommen, ein Thread liest regelmäßig den Namen:

System.out.println(user.getFirstName() + " " + user.getLastName());

Und zwei andere Threads kämpfen gegen ein Tauziehen und setzen es abwechselnd gegen Hillary Clinton und Donald Trump durch. Sie müssen jeweils zwei Methoden aufrufen. Meistens funktioniert das gut, aber hin und wieder sehen Sie einen Hillary Trump oder Donald Clinton vorbeiziehen.

Sie können dieses Problem nicht durch Hinzufügen einer Sperre innerhalb der Setter lösen, da die Sperre nur für die Dauer des Einstellens des Vor- oder Nachnamens gehalten wird. Die einzige Lösung durch Sperren ist das Hinzufügen einer Sperre um das gesamte Objekt, wodurch jedoch die Kapselung unterbrochen wird, da der aufrufende Code die Sperre verwalten muss (und Deadlocks verursachen kann).

Wie sich herausstellt, gibt es keine saubere Lösung durch Verriegelung. Die saubere Lösung besteht darin, die Einbauten erneut einzukapseln, indem sie gröber gemacht werden:

public class UserName {
   public final String first;
   public final String last;
   public UserName(String first, String last) { ... }
}

public class User
   private UserName name;
   public UserName getName() { return this.name; }
   public setName(UserName n) { this.name = n; }
}

Der Name selbst ist unveränderlich geworden, und Sie sehen, dass seine Mitglieder öffentlich sein können, da es sich jetzt um ein reines Datenobjekt handelt, das nach seiner Erstellung nicht mehr geändert werden kann. Im Gegenzug wurde die öffentliche API der Benutzerklasse mit nur einem Setter gröber, sodass der Name nur noch als Ganzes geändert werden kann. Es kapselt mehr seines internen Zustands hinter der API.

Was ist der Sinn des Ganzen in diesem Zyklus? Und hat die Kapselung in der realen Programmierung jetzt wirklich einen Sinn?

Was Sie in diesem Zyklus sehen, sind Versuche, Lösungen für eine bestimmte Reihe von Umständen zu breit anzuwenden. Ein geeigneter Kapselungsgrad setzt voraus, dass Sie die zu modellierende Domäne verstehen und den richtigen Kapselungsgrad anwenden. Manchmal bedeutet dies, dass alle Felder öffentlich sind, manchmal (wie in Akka-Anwendungen), dass Sie bis auf eine einzige Methode zum Empfangen von Nachrichten keine öffentliche API haben. Das Konzept der Kapselung selbst, dh das Verbergen von Interna hinter einer stabilen API, ist jedoch der Schlüssel zur Programmierung von Software in großem Maßstab, insbesondere in Systemen mit mehreren Threads.

Joeri Sebrechts
quelle
Fantastisch. Ich würde sagen, dass Sie die Diskussion um ein höheres Niveau angehoben haben. Vielleicht zwei. Ich dachte wirklich, dass ich die Verkapselung verstehe und manchmal besser als Standardbücher, und hatte wirklich eher Angst, dass wir sie aufgrund einiger akzeptierter Sitten und Technologien praktisch verlieren, und DAS war das eigentliche Thema meiner Frage (vielleicht nicht in einem Satz formuliert) guter Weg), aber Sie haben mir wirklich neue Seiten der Kapselung gezeigt. Ich bin definitiv nicht gut im Multitasking und Sie haben mir gezeigt, wie schädlich es sein kann, andere Grundprinzipien zu verstehen.
Gangnus
Aber ich kann Ihren Beitrag als nicht markieren die Antwort, denn es ist nicht über Massen Laden von Daten zu / von Objekten ist, das Problem , aufgrund derer solche Plattformen wie Bohnen und Lombok erscheinen. Vielleicht könnten Sie Ihre Gedanken in diese Richtung ausarbeiten, bitte, wenn Sie Zeit haben. Ich selbst muss die Konsequenzen, die Ihr Gedanke für dieses Problem mit sich bringt, noch überdenken. Und ich bin mir nicht sicher, ob ich fit genug dafür bin (
denk
Ich habe Lombok nicht verwendet, aber ich verstehe, dass es ein Weg ist, eine bestimmte Ebene der Kapselung (Getter / Setter in jedem Feld) mit weniger Typisierung zu implementieren. Es ändert nicht die ideale Form der API für Ihre Problemdomäne, es ist nur ein Werkzeug, um sie schneller zu schreiben.
Joeri Sebrechts
1

Ich kann mir einen Anwendungsfall vorstellen, bei dem dies sinnvoll ist. Möglicherweise haben Sie eine Klasse, auf die Sie ursprünglich über eine einfache Get- / Setter-API zugreifen. Sie erweitern oder ändern später, sodass nicht mehr dieselben Felder verwendet werden, sondern dasselbe API unterstützt wird .

Ein etwas ausgeklügeltes Beispiel: Ein Punkt, der als kartesisches Paar mit p.x()und beginnt p.y(). Sie erstellen später eine neue Implementierung oder Unterklasse, die Polarkoordinaten verwendet, sodass Sie auch p.r()und aufrufen können p.theta(), aber Ihren Clientcode, der aufruft p.x()und p.y()gültig bleibt. Die Klasse selbst konvertiert transparent von der internen Polarform, das heißt, y()würde jetzt return r * sin(theta);. (In diesem Beispiel ist die Einstellung nur x()oder y()nicht so sinnvoll, aber dennoch möglich.)

In diesem Fall könnte es sein, dass Sie sagen: "Ich bin froh, dass ich mich darum gekümmert habe, automatisch Getter und Setter zu deklarieren, anstatt die Felder zu veröffentlichen, oder ich hätte dort meine API auflösen müssen."

Davislor
quelle
2
Es ist nicht so schön, wie du es siehst. Das Ändern der inneren physischen Repräsentation macht das Objekt wirklich anders. Und es ist schlimm, dass sie gleich aussehen, denn sie haben unterschiedliche Qualitäten. Das Descartes-System hat keine besonderen Punkte, das Polarsystem hat einen.
Gangnus
1
Wenn Ihnen dieses spezielle Beispiel nicht gefällt, können Sie sich sicherlich andere vorstellen, die tatsächlich mehrere gleichwertige Darstellungen haben, oder eine neuere Darstellung, die in eine ältere und von einer älteren konvertiert werden kann.
Davislor
Ja, Sie können sie sehr produktiv für Präsentationen verwenden. In der Benutzeroberfläche kann es weit verbreitet sein. Aber zwingst du mich nicht, meine eigene Frage zu beantworten? :-)
Gangnus
Auf diese Weise effizienter, nicht wahr? :)
Davislor
0

Könnte mir jemand erklären, wie es ist, alle Bereiche als privat zu verstecken und sie anschließend durch eine zusätzliche Technologie freizulegen?

Es hat absolut keinen Sinn. Die Tatsache, dass Sie diese Frage stellen, zeigt jedoch, dass Sie nicht verstanden haben, was Lombok tut, und dass Sie nicht verstehen, wie OO-Code mit Kapselung geschrieben wird. Lassen Sie uns ein bisschen zurückspulen ...

Einige Daten für eine Klasseninstanz sind immer intern und sollten niemals verfügbar gemacht werden. Einige Daten für eine Klasseninstanz müssen extern festgelegt werden, und einige Daten müssen möglicherweise aus einer Klasseninstanz zurückgegeben werden. Wir möchten vielleicht ändern, wie die Klasse unter der Oberfläche arbeitet, also verwenden wir Funktionen, um Daten abzurufen und festzulegen.

Einige Programme möchten den Status für Klasseninstanzen speichern, daher verfügen diese möglicherweise über eine Serialisierungsschnittstelle. Wir fügen weitere Funktionen hinzu, mit denen die Klasseninstanz ihren Status im Speicher ablegen und ihren Status aus dem Speicher abrufen kann. Dadurch bleibt die Kapselung erhalten, da die Klasseninstanz immer noch die Kontrolle über ihre eigenen Daten hat. Möglicherweise serialisieren wir private Daten, aber der Rest des Programms hat keinen Zugriff darauf (oder genauer gesagt, wir pflegen eine chinesische Mauer, indem wir uns dafür entscheiden, diese privaten Daten nicht absichtlich zu beschädigen), und die Klasseninstanz kann (und sollte) Führen Sie Integritätsprüfungen bei der Deserialisierung durch, um sicherzustellen, dass die Daten wieder in Ordnung sind.

Manchmal müssen die Daten auf Bereich, Integrität oder ähnliches überprüft werden. Wenn wir diese Funktionen selbst schreiben, können wir das alles tun. In diesem Fall wollen oder brauchen wir Lombok nicht, weil wir das alles selbst machen.

Häufig stellen Sie jedoch fest, dass ein extern eingestellter Parameter in einer einzelnen Variablen gespeichert ist. In diesem Fall benötigen Sie vier Funktionen, um den Inhalt dieser Variablen abzurufen / festzulegen / zu serialisieren / zu deserialisieren. Wenn Sie diese vier Funktionen jedes Mal selbst schreiben, werden Sie langsamer und sind fehleranfällig. Die Automatisierung des Prozesses mit Lombok beschleunigt Ihre Entwicklung und beseitigt die Möglichkeit von Fehlern.

Ja, es wäre möglich, diese Variable öffentlich zu machen. In dieser speziellen Codeversion wäre sie funktional identisch. Aber gehen Sie zurück zu dem Grund, warum wir Funktionen verwenden: "Wir möchten vielleicht ändern, wie die Klasse unter der Oberfläche arbeitet ..." Wenn Sie Ihre Variable öffentlich machen, beschränken Sie Ihren Code jetzt und für immer darauf, diese öffentliche Variable als zu haben die Schnittstelle. Wenn Sie jedoch Funktionen verwenden oder Lombok verwenden, um diese Funktionen automatisch für Sie zu generieren, können Sie die zugrunde liegenden Daten und die zugrunde liegende Implementierung jederzeit in der Zukunft ändern.

Macht das alles klarer?

Graham
quelle
Sie sprechen über die Fragen des Erstsemesters. Wir sind schon woanders. Ich würde das niemals sagen und dir sogar ein Plus für kluge Antworten geben, wenn du nicht so unhöflich in Form deiner Antwort wärst.
Gangnus
0

Ich bin eigentlich kein Java-Entwickler. aber das folgende ist ziemlich plattformunabhängig.

Fast alles, was wir schreiben, verwendet öffentliche Getter und Setter, die auf private Variablen zugreifen. Die meisten Getter und Setter sind trivial. Aber wenn wir entscheiden, dass der Setter etwas neu berechnen muss oder der Setter eine Validierung durchführt oder die Eigenschaft an eine Eigenschaft einer Mitgliedsvariablen dieser Klasse weiterleiten muss, ist dies für den gesamten Code völlig unterbrechungsfrei und daher binärkompatibel kann das eine Modul austauschen.

Wenn wir entscheiden, dass diese Eigenschaft wirklich im Handumdrehen berechnet werden soll, muss sich der gesamte Code, der sie anzeigt, nicht ändern, und nur der Code, der darauf schreibt, muss sich ändern, und die IDE kann ihn für uns finden. Wenn wir uns dazu entschließen, dass es sich um ein beschreibbares berechnetes Feld handelt (das nur ein paar Mal durchgeführt werden musste), können wir das auch. Das Schöne ist, dass einige dieser Änderungen binärkompatibel sind (Änderungen am schreibgeschützten berechneten Feld sind zwar nicht theoretisch, können aber in der Praxis vorkommen).

Wir haben am Ende viele, viele Kleinigkeiten mit komplizierten Setzern. Wir hatten auch einige Caching-Getter. Das Endergebnis ist, dass Sie davon ausgehen dürfen, dass Getter einigermaßen billig sind, Setter jedoch möglicherweise nicht. Andererseits sind wir klug genug zu entscheiden, dass Setter nicht auf der Festplatte bleiben.

Aber ich musste den Typen ausfindig machen, der blindlings alle Mitgliedsvariablen in Eigenschaften änderte. Er wusste nicht, was atomare Addition ist, also änderte er das, was wirklich eine öffentliche Variable sein musste, in eine Eigenschaft und brach den Code auf subtile Weise.

Joshua
quelle
Willkommen in der Java-Welt. (C # auch). *Seufzer*. Seien Sie jedoch vorsichtig, wenn Sie get / set zum Ausblenden der inneren Repräsentation verwenden. Es handelt sich nur um ein externes Format, es ist in Ordnung. Aber wenn es um Mathematik oder, schlimmer noch, Physik oder andere reale Dinge geht, hat eine andere innere Repräsentation andere Qualitäten. Viz mein Kommentar zu softwareengineering.stackexchange.com/a/358662/44104
Gangnus
@Gangnus: Dieses Beispiel ist am unglücklichsten. Wir hatten schon einige davon, aber die Berechnung war immer genau.
Joshua
-1

Getter und Setter sind eine "Nur-für-Fall" -Maßnahme, die hinzugefügt wurde, um künftiges Refactoring zu vermeiden, wenn sich die interne Struktur oder die Zugriffsanforderungen während des Entwicklungsprozesses ändern.

Angenommen, einige Monate nach der Freigabe informiert Sie Ihr Client, dass eines der Felder einer Klasse manchmal auf negative Werte gesetzt ist, obwohl es in diesem Fall höchstens auf 0 geklemmt werden sollte. Wenn Sie öffentliche Felder verwenden, müssen Sie in Ihrer gesamten Codebasis nach jeder Zuordnung dieses Felds suchen, um die Klemmfunktion auf den Wert anzuwenden, den Sie festlegen möchten. Beachten Sie, dass Sie dies immer tun müssen, wenn Sie dieses Feld ändern. aber das nervt. Wenn Sie stattdessen bereits Getter und Setter verwendet hätten, hätten Sie einfach Ihre setField () -Methode ändern können, um sicherzustellen, dass diese Klemmung immer angewendet wird.

Das Problem mit "antiquierten" Sprachen wie Java ist, dass sie die Verwendung von Methoden für diesen Zweck anregen, was Ihren Code nur unendlich ausführlicher macht. Es ist schmerzhaft zu schreiben und schwer zu lesen, weshalb wir IDE verwenden, die dieses Problem auf die eine oder andere Weise mildert. Die meisten IDEs generieren automatisch Getter und Setter für Sie und verbergen sie, sofern nicht anders angegeben. Lombok geht noch einen Schritt weiter und generiert sie einfach prozedural während der Kompilierung, um Ihren Code besonders glatt zu halten. Andere modernere Sprachen haben dieses Problem jedoch auf die eine oder andere Weise einfach gelöst. Scala- oder .NET-Sprachen, zum Beispiel

In VB .NET oder C # könnten Sie beispielsweise einfach alle Felder so einrichten, dass sie nur öffentliche Felder enthalten, und sie später als privat kennzeichnen, ihren Namen ändern und eine Eigenschaft mit dem vorherigen Namen von verfügbar machen das Feld, in dem Sie das Zugriffsverhalten des Felds bei Bedarf anpassen können. Wenn Sie in Lombok das Verhalten eines Get- oder Setters optimieren müssen, können Sie diese Tags bei Bedarf einfach entfernen und Ihre eigenen mit den neuen Anforderungen codieren, da Sie sicher sind, dass Sie in anderen Dateien keine Änderungen vornehmen müssen.

Grundsätzlich sollte die Art und Weise, wie Ihre Methode auf ein Feld zugreift, transparent und einheitlich sein. In modernen Sprachen können Sie "Methoden" mit der gleichen Zugriffs- / Aufrufsyntax eines Felds definieren, sodass diese Änderungen bei Bedarf vorgenommen werden können, ohne sich während der frühen Entwicklung Gedanken zu machen habe diese Funktion nicht. Alles, was Lombok tut, ist, Ihnen Zeit zu sparen, da Sie mit der von Ihnen verwendeten Sprache keine Zeit für eine "nur für den Fall" -Methode sparen möchten.

Schreckliche Person
quelle
In diesen "modernen" Sprachen sieht die API wie ein Feldzugriff aus, wird foo.barjedoch möglicherweise von einem Methodenaufruf verarbeitet. Sie behaupten, dies sei der "veralteten" Art überlegen, eine API wie Methodenaufrufe aussehen zu lassenfoo.getBar() . Wir scheinen zuzustimmen, dass öffentliche Felder problematisch sind, aber ich behaupte, dass die "antiquierte" Alternative der "modernen" überlegen ist, da unsere gesamte API symmetrisch ist (es sind alles Methodenaufrufe). Im "modernen" Ansatz müssen wir entscheiden, welche Dinge Eigenschaften und welche Methoden sein sollen, was alles überkompliziert (besonders wenn wir Reflexion verwenden!).
Warbo
1
1. Physik zu verstecken ist nicht immer so gut. Schau meinen Kommentar an softwareengineering.stackexchange.com/a/358662/44104 . 2. Ich spreche nicht darüber, wie schwer oder einfach die Erstellung von Gettern / Setzern ist. Das C # hat absolut das gleiche Problem, von dem wir sprechen. Das Fehlen einer Einkapselung aufgrund der schlechten Lösung beim Laden von Masseninformationen. 3. Ja, die Lösung könnte auf der Syntax basieren, aber bitte auf andere Weise als von Ihnen erwähnt. Die sind schlecht, wir haben hier eine große Seite mit Erklärungen gefüllt. 4. Die Lösung muss nicht auf der Syntax basieren. Dies könnte in Java-Grenzen erfolgen.
Gangnus
-1

Könnte mir jemand erklären, was es heißt, alle Bereiche als privat zu verstecken und sie anschließend durch eine zusätzliche Technologie freizulegen? Warum benutzen wir dann einfach nicht nur öffentliche Felder? Ich habe das Gefühl, dass wir einen langen und schweren Weg zurückgelegt haben, nur um zum Ausgangspunkt zurückzukehren.

Ja, das ist widersprüchlich. Ich bin zuerst auf Eigenschaften in Visual Basic gestoßen. Bis dahin gab es meiner Erfahrung nach in anderen Sprachen keine Eigenschaftsumhüllungen um Felder. Nur öffentliche, private und geschützte Felder.

Eigenschaften waren eine Art Einkapselung. Ich habe unter Visual Basic-Eigenschaften eine Möglichkeit verstanden, die Ausgabe eines oder mehrerer Felder zu steuern und zu bearbeiten, während das explizite Feld und sogar sein Datentyp ausgeblendet werden, beispielsweise das Ausgeben eines Datums als Zeichenfolge in einem bestimmten Format. Aber auch dann ist man nicht "Versteckspiel und Enthüllungsspiel" aus der Perspektive eines größeren Objekts.

Eigenschaften rechtfertigen sich jedoch durch getrennte Eigenschafts-Getter und -Setter. Ein Feld ohne eine Eigenschaft auszustellen war alles oder nichts - wenn Sie es lesen könnten, könnten Sie es ändern. So kann man jetzt schwache Klassen mit geschützten Setzern rechtfertigen.

Also, warum haben wir / sie nicht nur tatsächliche Methoden angewendet? Weil es Visual Basic (und VBScript ) (oooh! Aaah!) War, das für die Massen (!) Kodierte, und es war der letzte Schrei. Und so dominierte schließlich eine Idiotokratie.

Radarbob
quelle
Ja, Eigenschaften für die Benutzeroberfläche haben einen besonderen Sinn, sie sind öffentlich und hübsche Darstellungen der Felder. Guter Punkt.
Gangnus
"Also, warum haben wir / sie nicht nur tatsächliche Methoden angewendet?" Technisch taten sie in der .Net-Version. Eigenschaften sind syntaktische Zucker für get- und set-Funktionen.
Pharap
"Idiotokratie" ? Meinst du nicht " Idiosynkrasie "?
Peter Mortensen
Ich meinte Idiokratie
Radarbob