Was ist ein genaues Problem beim Zulassen von Gettern?


15

Ich bin nicht auf der Suche nach einer Meinung zur Semantik, sondern einfach nach einem Fall, in dem ein vernünftiger Einsatz von Gettern ein tatsächliches Hindernis darstellt. Vielleicht stürzt es mich in eine endlose Spirale, mich auf sie zu verlassen, vielleicht ist die Alternative sauberer und geht automatisch mit Gettern um usw. Etwas Konkretes.

Ich habe alle Argumente gehört, ich habe gehört, dass sie schlecht sind, weil sie Sie dazu zwingen, Objekte als Datenquellen zu behandeln, dass sie den "reinen Zustand" eines Objekts verletzen von "Geben Sie nicht zu viel heraus, sondern seien Sie bereit dazu viel akzeptieren ".

Aber absolut kein vernünftiger Grund, warum a getDataeine schlechte Sache ist, in der Tat argumentierten einige Leute, dass es viel um Semantik geht, besser als per se, aber nenne sie einfach nicht getX, für mich ist das zumindest lustig .

Was ist eine Sache ohne Meinungen, die kaputt geht, wenn ich Getter vernünftig einsetze und Daten, bei denen die Integrität des Objekts eindeutig nicht kaputt geht, wenn es gelöscht wird?

Natürlich ist es nicht blöd, einen Getter für einen String zuzulassen, mit dem etwas verschlüsselt wird, aber ich spreche von Daten, die Ihr System benötigt, um zu funktionieren. Möglicherweise werden Ihre Daten Provideraus dem Objekt durch a abgerufen, aber das Objekt muss immer noch zulassen Provider, dass a ausgeführt $provider[$object]->getDatawird. Es führt kein Weg daran vorbei.


Warum ich frage: Für mich werden Getter, wenn sie vernünftig verwendet werden und wenn Daten, die als "sicher" behandelt werden, von Gott gesendet werden, 99% meiner Getter verwendet, um das Objekt zu identifizieren, wie in, frage ich, durch Code Object, what is your name? Object, what is your identifier?, Jeder, der mit einem Objekt arbeitet, sollte diese Dinge über ein Objekt wissen, denn fast alles in der Programmierung ist Identität und wer weiß besser, was es ist als das Objekt selbst? Ich sehe keine wirklichen Probleme, es sei denn, Sie sind Purist.

Ich habe mir alle StackOverflow-Fragen zum Thema "Warum Getter / Setter" angeschaut und obwohl ich der Meinung bin, dass Setter in 99% der Fälle wirklich schlecht sind, müssen Getter nicht gleich behandelt werden, nur weil sie sich reimen.

Ein Setter wird die Identität Ihres Objekts gefährden und es sehr schwer machen, zu debuggen, wer die Daten ändert, aber ein Getter unternimmt nichts.


2
Mein Rat ist, den größten Teil der OO-Theorie zu vergessen. Trainieren. Schreiben Sie viel Code und pflegen Sie ihn anschließend. Sie werden viel, viel, viel mehr darüber lernen, was gut funktioniert und was nicht, wenn Sie etwas tun und einige Monate später darauf zurückkommen.
jpmc26

1
@ jpmc26 Was bei der Mutter von ... Mir ist eigentlich nie aufgefallen, dass keiner von uns OOP wirklich richtig macht. Ich hatte immer den Begriff der Kapselung, die sich ausschließlich auf die Felder eines Objekts bezieht, aber das Objekt selbst ist ein Zustand und ist ein Sein frei geteilt. Wirklich jeder macht OOP falsch, oder besser gesagt, OOP ist nicht möglich, wenn das Paradigma Objekte sind. Ich verwende OOP ausschließlich, um auszudrücken, wie ein System funktioniert, und behandle es nie als den heiligen Gral, noch als SOLID. Es sind nur Werkzeuge, mit denen ich hauptsächlich eine einigermaßen fundierte (durch die Implementierung) Umsetzung meines Konzepts definiere. Irgendwelche liest weiter, wenn dies richtig ist?
Coolpasta

2
--cont, Je mehr ich schreibe, desto mehr ist mir klar, dass es beim Programmieren darum geht, so viel zu abstrahieren, wie es erforderlich ist , um mit anderen und Ihnen über Ihre Codebasis zu argumentieren. Die technische Seite dabei ist natürlich, dass Sie die richtigen Prüfungen durchführen / die richtigen Regeln festlegen. In Wahrheit gewinnt jeder, der es schafft, dass andere ihren Code besser verstehen. Stellen Sie sicher, dass es für jeden, der es liest, sinnvoll ist, und stellen Sie dann sicher, dass es nicht fehlschlägt, indem Sie Objekte mit Schnittstellen richtig segmentieren, überprüfen und zu anderen Paradigmen wechseln, wenn es einfacher ist, mit ihnen zu argumentieren: Seien Sie flexibel.
Coolpasta

2
Das kommt mir wie ein Strohmann vor. Ich glaube nicht, dass ein ernsthafter Entwickler, der tatsächlich brauchbaren Code schreibt, argumentieren würde, dass der vernünftige Einsatz von Gettern ein Problem darstellt. Die Frage impliziert, dass Getter sinnvoll eingesetzt werden können, und die Frage impliziert, dass Getter sinnvoll eingesetzt werden können. Suchen Sie jemanden, der sagt, dass es keinen vernünftigen Gebrauch von Gettern gibt? Sie werden eine Weile warten.
JimmyJames

2
@coolpasta Du hast recht. Es ist mir eigentlich egal, ob es reines OOP ist. Einfach besser. Geben Sie mir die Wahl zwischen fehlerhaftem Code, der von Menschen verstanden werden kann, und fehlerlosem CPU-liebendem Code, der nicht zu entziffern ist, und ich nehme jedes Mal den benutzerfreundlichen Code. OOP ist NUR nützlich für mich, wenn es die Anzahl der Dinge verringert, über die ich gleichzeitig nachdenken muss. Das ist der einzige Grund, warum ich es so einfach finde. Ohne das lässt du mich ohne Grund durch mehrere Quellcode-Dateien springen.
candied_orange

Antworten:


22

Ohne Getter kann man keinen guten Code schreiben.

Der Grund dafür ist nicht, dass Getter die Kapselung nicht brechen, sondern dass sie es tun. Es liegt nicht daran, dass Getter die Leute nicht dazu verleiten, OOP nicht zu folgen, wodurch sie Methoden mit den Daten setzen müssten, auf die sie einwirken. Tun sie. Nein, du brauchst Getter wegen der Grenzen.

Die Idee der Kapselung und des Zusammenhaltens von Methoden mit den Daten, auf die sie wirken, funktioniert einfach nicht, wenn Sie auf eine Grenze stoßen, die Sie vom Verschieben einer Methode abhält und Sie daher zum Verschieben von Daten zwingt.

Es ist wirklich so einfach. Wenn Sie Getter verwenden, wenn es keine Grenze gibt, haben Sie am Ende keine realen Objekte. Alles beginnt sich dem Verfahren anzunähern. Welches funktioniert so gut wie nie zuvor.

True OOP kann man nicht überall verbreiten. Es funktioniert nur innerhalb dieser Grenzen.

Diese Grenzen sind nicht hauchdünn. Sie haben Code in ihnen. Dieser Code kann nicht OOP sein. Es kann auch nicht funktionieren. Nein, dieser Code hat unsere Ideale gestrippt, damit er mit der harten Realität umgehen kann.

Michael Fetters nannte diese Code- Faszie nach dem weißen Bindegewebe, das Teile einer Orange zusammenhält.

Dies ist eine wunderbare Art, darüber nachzudenken. Es wird erklärt, warum es in Ordnung ist, beide Arten von Code in derselben Codebasis zu haben. Ohne diese Perspektive klammern sich viele neue Programmierer hart an ihre Ideale, haben dann das Herz gebrochen und geben diese Ideale auf, wenn sie an ihre erste Grenze stoßen.

Die Ideale funktionieren nur an ihrem richtigen Ort. Gib sie nicht auf, nur weil sie nicht überall funktionieren. Verwenden Sie sie dort, wo sie arbeiten. Dieser Ort ist der saftige Teil, den die Faszie schützt.

Ein einfaches Beispiel für eine Grenze ist eine Sammlung. Dies hält etwas und hat keine Ahnung, was es ist. Wie könnte ein Kollektionsdesigner möglicherweise die Verhaltensfunktionalität des gehaltenen Objekts in die Sammlung verschieben, wenn er keine Ahnung hat, was es halten wird? Das kannst du nicht. Sie stoßen an eine Grenze. Deshalb haben Kollektionen Getter.

Wenn Sie das wüssten, könnten Sie dieses Verhalten verschieben und vermeiden, den Status zu verschieben. Wenn Sie es wissen, sollten Sie es tun. Du weißt es einfach nicht immer.

Manche Leute nennen das einfach pragmatisch. Und es ist. Aber es ist schön zu wissen, warum wir pragmatisch sein müssen.


Sie haben zum Ausdruck gebracht, dass Sie semantische Argumente nicht hören wollen, und scheinen zu befürworten, überall "vernünftige Getter" zu setzen. Sie möchten, dass diese Idee in Frage gestellt wird. Ich denke, ich kann zeigen, dass die Idee Probleme mit der Art und Weise hat, wie Sie sie eingerahmt haben. Ich glaube aber auch, dass ich weiß, woher du kommst, weil ich dort war.

Wenn Sie überall Getters wollen, schauen Sie sich Python an. Es gibt kein privates Schlüsselwort. Dennoch macht Python OOP ganz gut. Wie? Sie benutzen einen semantischen Trick. Sie nennen alles, was privat sein soll, mit einem führenden Unterstrich. Sie dürfen sogar davon lesen, vorausgesetzt, Sie übernehmen die Verantwortung dafür. "Wir sind alle Erwachsene hier", sagen sie oft.

Was ist also der Unterschied zwischen dem, alles in Java oder C # zu verbessern? Tut mir leid, aber es ist Semantik. Pythons unterstreichen die Konvention und signalisieren Ihnen deutlich, dass Sie hinter der Tür des Mitarbeiters herumstöbern. Schlagen Sie auf alles und Sie verlieren dieses Signal. Mit Reflektion hätten Sie das Private sowieso abstreifen und trotzdem nicht das semantische Signal verlieren können. Hier gibt es einfach kein strukturelles Argument.

Wir müssen also entscheiden, wo das Schild "nur für Angestellte" aufgehängt werden soll. Was ist als privat zu betrachten? Sie nennen das "vernünftige Getter". Wie ich bereits sagte, ist die beste Rechtfertigung für einen Getter eine Grenze, die uns von unseren Idealen abhält. Das sollte nicht dazu führen, dass alles besser wird. Wenn es zu einem Getter kommt, sollten Sie erwägen, das Verhalten weiter in das saftige Bit zu verschieben, in dem Sie es schützen können.

Diese Trennung hat zu einigen Begriffen geführt. Ein Datenübertragungsobjekt oder DTO enthält kein Verhalten. Die einzigen Methoden sind Getter und manchmal Setter, manchmal Konstruktoren. Dieser Name ist bedauerlich, weil er überhaupt kein echtes Objekt ist. Die Getter und Setter sind eigentlich nur Debugging-Codes, mit denen Sie einen Haltepunkt setzen können. Ohne diese Notwendigkeit wären sie nur ein Haufen öffentlicher Felder. In C ++ haben wir sie Structs genannt. Der einzige Unterschied zu einer C ++ - Klasse bestand darin, dass sie standardmäßig öffentlich waren.

DTOs sind nett, weil Sie sie über eine Grenzmauer werfen und Ihre anderen Methoden sicher in einem netten saftigen Verhaltensobjekt aufbewahren können. Ein wahres Objekt. Es gibt keine Getter, die die Kapselung verletzen könnten. Meine Verhaltensobjekte können DTOs essen, indem sie als Parameterobjekte verwendet werden . Manchmal muss ich eine defensive Kopie davon erstellen , um einen gemeinsamen veränderlichen Zustand zu verhindern . Ich verbreite keine veränderlichen DTOs innerhalb des saftigen Teils innerhalb der Grenze. Ich kapsle sie ein. Ich verstecke sie. Und wenn ich endlich an eine neue Grenze stoße, drehe ich ein neues DTO auf und werfe es über die Mauer, wodurch es das Problem eines anderen macht.

Aber Sie möchten Gettern zur Verfügung stellen, die Identität ausdrücken. Nun, Glückwunsch, Sie haben eine Grenze gefunden. Entitäten haben eine Identität, die über ihre Referenz hinausgeht. Das heißt, jenseits ihrer Speicheradresse. Also muss es irgendwo aufbewahrt werden. Und etwas muss in der Lage sein, sich anhand seiner Identität auf dieses Ding zu beziehen. Ein Getter, der Identität ausdrückt, ist durchaus vernünftig. Ein Haufen Code, der diesen Getter verwendet, um Entscheidungen zu treffen, die die Entität selbst hätte treffen können, ist dies nicht.

Am Ende ist es nicht die Existenz von Gettern, die falsch ist. Sie sind viel besser als öffentliche Felder. Schlecht ist, wenn sie so tun, als wären Sie objektorientiert, wenn Sie es nicht sind. Getter sind gut. Objektorientiert zu sein ist gut. Getter sind nicht objektorientiert. Verwenden Sie Getter, um einen sicheren Ort für die Objektorientierung zu finden.


Genau deshalb habe ich gefragt und ich bin mir nicht sicher, ob mein Gespräch mit Robert sichtbar ist. Ich habe auch versucht, einen bestimmten Fall herauszufinden, in dem es mir eindeutig weh tut, "vernünftige Getter" zu verwenden. Ich bin mit den Setzern nicht einverstanden, da es zumindest für mich sehr schwierig ist, einer Aktion wie dieser das Eigentumsrecht zuzuschreiben. Ich habe Setzer in allem verwendet und als ich merkte, dass sogar ein Setzer, der von zu vielen Objekten betroffen ist, tötet Ich musste alles neu schreiben. Der vernünftige Einsatz von Gettern hat nach so langen Tests keine Nachteile, wenn Sie kein Purist sind.
Coolpasta

1
@coolpasta können Sie Dateneigentümerobjekte erstellen. Dateneignerobjekte sind Datenobjekte, aber sie sind so entworfen und benannt, dass sie (eindeutig) Eigentümer der Daten sind. Natürlich werden diese Objekte Getter haben.
5.

1
@rwong Was meinst du mit clearly? Wenn Daten von einem Wert an mich übergeben werden, besitzt mein Objekt jetzt die Daten, aber von der Semantik her hat es noch nicht die Daten von jemand anderem. Es muss dann Operationen ausführen, um diese Daten zu transformieren. Sobald die Daten berührt werden, müssen Sie semantische Änderungen vornehmen: Sie haben Daten mit dem Namen $data_from_requestund jetzt haben Sie sie bearbeitet? Nennen Sie es $changed_data. Sie möchten diese Daten an Dritte weitergeben? Erstellen Sie einen Getter getChangedRequestData, dann ist der Besitz eindeutig festgelegt. Oder ist es?
coolpasta

1
"Ohne Getter kann man keinen guten Code schreiben." Dies ist absolut falsch, ebenso wie die Vorstellung, dass Grenzen von Natur aus Getter benötigen. Das ist kein Pragmatismus, nur Faulheit. Es ist viel einfacher, Daten einfach über den Zaun zu werfen und damit fertig zu werden. Über Anwendungsfälle nachzudenken und eine gute API für das Verhalten zu entwerfen, ist schwierig.
Robert Bräutigam

2
Hervorragende Antwort!
cmaster

10

Getter verstoßen gegen das Hollywood-Prinzip ("Ruf uns nicht an, wir rufen dich an")

Das Hollywood-Prinzip (auch bekannt als Inversion of Control) besagt, dass Sie keinen Bibliothekscode aufrufen, um Dinge zu erledigen. Vielmehr ruft das Framework Ihren Code auf. Da das Framework die Dinge steuert, ist es nicht erforderlich, den internen Status an die Clients zu senden. Das musst du nicht wissen.

In seiner heimtückischsten Form bedeutet ein Verstoß gegen das Hollywood-Prinzip, dass Sie einen Getter verwenden, um Informationen über den Status einer Klasse abzurufen, und dann anhand des erhaltenen Werts entscheiden, welche Methoden für diese Klasse aufgerufen werden sollen. es ist eine Verletzung der Kapselung vom Feinsten.

Die Verwendung eines Getters impliziert, dass Sie diesen Wert benötigen, wenn Sie dies tatsächlich nicht tun.

Möglicherweise benötigen Sie diese Leistungsverbesserung tatsächlich

In extremen Fällen von leichten Objekten, die die maximal mögliche Leistung aufweisen müssen, ist es möglich (wenn auch äußerst unwahrscheinlich), dass Sie die sehr geringe Leistungsstrafe, die ein Getter verhängt, nicht zahlen können. Dies wird in 99,9 Prozent der Fälle nicht der Fall sein.


1
Ich verstehe und ich werde mit deiner Wahrheit fliegen, die ich nicht wissen muss. . Aber ich habe einen Punkt erreicht, an dem ich muss. Ich habe ein GeneratorObjekt, das alle meine ItemsObjekte durchläuft und dann getNamevon jedem aufruft Item, etwas weiter zu machen. Was ist das Problem damit? Im Gegenzug Generatorspucken die formatierten Zeichenfolgen aus. Dies liegt in meinem Framework, für das ich dann zusätzlich eine API habe, mit der Benutzer alles ausführen können, was die Benutzer bereitstellen, ohne das Framework zu berühren.
Coolpasta

3
What is the issue with this?Keine, die ich sehen kann. Genau das macht eine mapFunktion. Aber das ist nicht die Frage, die Sie gestellt haben. Sie fragte im Wesentlichen „Gibt es irgendwelche Bedingungen , unter denen ein Getter nicht ratsam sein könnte.“ Ich antwortete mit zwei, aber das heißt nicht, dass Sie die Setter ganz aufgeben.
Robert Harvey

1
Sie stellen dem Falschen diese Frage. Ich bin Pragmatiker. Ich mache, was für meine spezifischen Programme am besten geeignet ist, und lege nur dann viel Wert auf "Grundsätze", wenn sie meinen Zwecken dienen.
Robert Harvey

2
@ jpmc26: Frameworks. Keine Bibliotheken.
Robert Harvey

2
Ein Framework ist ein Versuch, eine Allzwecksprache in eine domänenspezifische Sprache umzuwandeln. Eine Bibliothek ist eine Sammlung wiederverwendbarer Algorithmen. Beide werden Sie bitten, sich darauf zu verlassen. Es liegt an Ihnen, ob Sie die Abhängigkeit fest programmieren oder einfach austauschbar machen möchten.
candied_orange

2

Bekommt Leck-Implementierungsdetails und unterbricht Abstraktion

Erwägen public int millisecondsSince1970()

Ihre Kunden werden denken : „Oh, das ist ein int“ und es wird eine Unmenge Anrufe werden unter der Annahme , dass es eine ist int , integer math tun Daten zu vergleichen, etc ... Wenn Sie feststellen , dass Sie eine brauchen lange , es wird eine Menge sein von Legacy-Code mit Problemen. In einer Java-Welt würden Sie @deprecateddie API erweitern, jeder wird sie ignorieren, und Sie müssen veralteten, fehlerhaften Code beibehalten. :-( (Ich gehe davon aus, dass andere Sprachen ähnlich sind!)

In diesem speziellen Fall bin ich mir nicht sicher, was eine bessere Option sein könnte, und die Antwort von @candiedorange ist völlig zutreffend. Es gibt viele Male, wenn Sie Getter benötigen, aber dieses Beispiel zeigt die Nachteile. Jeder öffentliche Getter neigt dazu, Sie an eine bestimmte Implementierung zu "binden". Verwenden Sie sie, wenn Sie wirklich "Grenzen überschreiten" müssen, aber so wenig wie möglich und mit Sorgfalt und Umsicht.


Dies ist wahr, aber wie lautet die Musterlösung für die Erzwingung eines Datentyps / einer Datenstruktur für die Variable eines Objekts sowie für alles, was den Getter für diese Variable verwendet?
Coolpasta

1
Dieses Beispiel zeigt ein anderes Phänomen, die primitive Besessenheit. Heutzutage haben die meisten Klassen - Bibliotheken und Frameworks , die darstellen timepointund timespanjeweils. ( epochist ein besonderer Wert von timepoint.) In diesen Klassen bieten sie Getter wie getIntoder getLongoder getDouble. Wenn Klassen, die die Zeit manipulieren, so geschrieben werden, dass sie (1) zeitbezogene Arithmetik an diese Objekte und Methoden delegieren und (2) über Getter Zugriff auf diese Zeitobjekte gewähren, ist das Problem gelöst, ohne Getter loswerden zu müssen .
rwong

1
Zusammenfassend ist festzuhalten, dass Getter Teil der API sind. Daher müssen Getter mit ausreichenden Überlegungen zu allen Konsequenzen entworfen werden, einschließlich vergangener, aktueller und zukünftiger Konsequenzen. Dies führt jedoch nicht zu der Schlussfolgerung, die Anzahl der Getter zu vermeiden oder zu minimieren. Wenn zukünftig auf eine bestimmte Information zugegriffen werden muss, für die ein Getter weggelassen wurde, wäre eine Änderung der API und des Codes auf Bibliotheksseite erforderlich gewesen. Daher sollte die Entscheidung, ob ein bestimmter Getter implementiert oder vermieden werden soll, auf der Domäne und Absicht beruhen, nicht auf präventiven Gründen.
Rwong

1
"millisecondsSince1970" hat vor Ende Januar 1970 für 32-Bit-Ints aufgehört zu arbeiten, daher bezweifle ich, dass viel Code verwendet wird :-)
gnasher729

1
@rwong Etwas korrekt, aber Sie gehen davon aus, dass die bevorzugten Bibliotheken, die Zeitpunkte darstellen, stabil sind. Was sie nicht sind. ZB moment.js
user949300

1

Ich denke, der erste Schlüssel ist, sich daran zu erinnern, dass Absolutes immer falsch ist.

Stellen Sie sich ein Adressbuchprogramm vor, in dem einfach die Kontaktinformationen der Benutzer gespeichert werden. Aber machen wir es richtig und unterteilen es in die Ebenen Controller / Service / Repository.

Es wird eine PersonKlasse geben, die aktuelle Daten enthält: Name, Adresse, Telefonnummer usw., Addressund Phonees handelt sich auch um Klassen, sodass wir später den Polymorphismus verwenden können, um Schemata außerhalb der USA zu unterstützen. Alle drei Klassen sind Datenübertragungsobjekte , daher sind sie nichts anderes als Getter und Setter. Und das macht Sinn: Sie werden keine Logik in sich haben, die über das Überschreiben von equalsund hinausgeht getHashcode. Die Aufforderung, Ihnen eine Darstellung der vollständigen Personendaten zu geben, ist nicht sinnvoll: Ist dies für HTML-Präsentationen, eine benutzerdefinierte GUI-App, eine Konsolen-App, einen HTTP-Endpunkt usw. sinnvoll?

Hier kommen der Controller, der Service und das Repository ins Spiel. Alle diese Klassen werden dank der Abhängigkeitsinjektion logiklastig und getterleicht sein.

Der Controller wird einen Dienst injizieren lassen, der Methoden wie getund verfügbar macht, die savebeide einige vernünftige Argumente benötigen (z. B. getÜberschreibungen für die Suche nach Namen, Postleitzahl oder einfach alles saveabrufen) eine einzelne Personund speichern Sie es). Welche Stärken hätte der Controller? In der Lage zu sein, den Namen der konkreten Klasse abzurufen, kann für die Protokollierung hilfreich sein. In welchem ​​anderen Status als dem Status, der in die Klasse injiziert wurde, wird sie gespeichert?

In ähnlicher Weise wird der Service-Schicht ein Repository hinzugefügt, so dass sie tatsächlich Persons abrufen und beibehalten kann , und sie kann eine gewisse "Geschäftslogik" aufweisen (z. B. müssen möglicherweise alle PersonObjekte mindestens eine Adresse oder ein Telefon haben) Nummer). Aber noch einmal: Welchen Zustand hat dieses Objekt anders als das, was injiziert wurde? Wieder keine.

Auf der Repository-Ebene wird es etwas interessanter: Sie stellt eine Verbindung zu einem Speicherort her, z. Eine Datei, eine SQL-Datenbank oder ein In-Memory-Speicher. Hier ist es verlockend, einen Getter hinzuzufügen, um den Dateinamen oder die SQL-Verbindungszeichenfolge abzurufen, aber dieser Status wurde in die Klasse eingefügt. Soll das Repository einen Getter haben, der feststellt, ob die Verbindung zu seinem Datenspeicher erfolgreich hergestellt wurde oder nicht? Nun, vielleicht, aber wozu dienen diese Informationen außerhalb der Repository-Klasse selbst? Die Repository-Klasse selbst sollte bei Bedarf versuchen können, erneut eine Verbindung zu ihrem Datenspeicher herzustellen, was darauf hindeutet, dass die isConnectedEigenschaft bereits einen zweifelhaften Wert hat. Darüber hinaus wird eine isConnectedEigenschaft wahrscheinlich genau dann falsch sein, wenn sie richtig sein müsste: ÜberprüfenisConnectedvor dem Versuch, Daten abzurufen / zu speichern, kann nicht garantiert werden, dass das Repository weiterhin verbunden ist, wenn der "echte" Aufruf erfolgt, sodass die Notwendigkeit einer Ausnahmebehandlung nicht beseitigt wird (es gibt darüber hinaus Argumente dafür, wohin dies führen sollte) den Umfang dieser Frage).

Berücksichtigen Sie auch Unit-Tests (Sie schreiben Unit-Tests, oder?): Der Service erwartet nicht, dass eine bestimmte Klasse injiziert wird, sondern vielmehr, dass eine konkrete Implementierung einer Schnittstelle injiziert wird. Auf diese Weise kann die Anwendung als Ganzes ändern, wo die Daten gespeichert werden, ohne dass etwas anderes als das in den Service eingespeiste Repository ausgetauscht werden muss. Außerdem kann der Service durch Injizieren eines Schein-Repository Unit-getestet werden. Dies bedeutet, in Schnittstellen anstatt in Klassen zu denken. Würde die Repository-Schnittstelle irgendwelche Getter aussetzen? Gibt es einen internen Status, der allen Repositorys gemeinsam ist, der außerhalb des Repositorys nützlich ist und nicht in das Repository eingespeist wird? Mir fällt es schwer, an mich selbst zu denken.

TL; DR

Fazit: Abgesehen von den Klassen, deren einziger Zweck es ist, Daten herumzutragen, hat nichts im Stapel einen Platz, um einen Getter zu setzen: state ist außerhalb der Arbeiterklasse entweder injiziert oder irrelevant.


Ich finde diese Denkweise sehr ähnlich zu der Debatte über die ordnungsgemäße Verwendung von C # -Eigenschaften. Kurz gesagt, von Eigenschaften (die Getter-Only- oder Getter-Setter-Pair-Eigenschaften sein können) wird erwartet, dass sie einen bestimmten Vertrag einhalten, z. "Getter sollte Fehler vermeiden" usw. Entgegen Ihrer Schlussfolgerung gibt es viele Objektzustände, die als Eigenschaften (oder Getter) verfügbar gemacht werden können. Beispiele sind zu zahlreich, um sie hier zu zitieren.
5.

2
@rwong Ich würde gerne einige Beispiele kennen, zum Beispiel für eine "normale" "Enterprise" -Anwendung, die von einem Team implementiert wurde. Nehmen wir der Einfachheit halber auch an, dass keine Dritten beteiligt sind. Sie wissen, ein in sich geschlossenes System, wie ein Kalender, eine Zoohandlung, was immer Sie wollen.
Robert Bräutigam

1

Was ist eine Sache ohne Meinungen, die kaputt geht, wenn ich Getter vernünftig einsetze und Daten, bei denen die Integrität des Objekts eindeutig nicht kaputt geht, wenn es gelöscht wird?

Veränderbare Mitglieder.

Wenn Sie eine Sammlung von Dingen in einem Objekt haben, die Sie über einen Getter verfügbar machen, setzen Sie sich möglicherweise Fehlern aus, die mit den falschen Dingen zusammenhängen, die der Sammlung hinzugefügt werden.

Wenn Sie ein veränderbares Objekt haben, das Sie über einen Getter verfügbar machen, können Sie sich dem internen Zustand Ihres Objekts auf eine Weise öffnen, die Sie nicht erwarten.

Ein wirklich schlechtes Beispiel wäre ein Objekt, das von 1 bis 100 zählt und seinen aktuellen Wert als veränderbare Referenz anzeigt. Dies ermöglicht einem Drittanbieterobjekt, den Wert von Current so zu ändern, dass der Wert außerhalb der erwarteten Grenzen liegen kann.

Dies ist meistens ein Problem beim Freilegen veränderlicher Objekte. Das Freilegen von Strukturen oder unveränderlichen Objekten ist überhaupt kein Problem, da Änderungen an diesen stattdessen eine Kopie ändern (normalerweise entweder durch eine implizite Kopie im Fall einer Struktur oder durch eine explizite Kopie im Fall eines unveränderlichen Objekts).

Verwenden Sie eine Metapher, mit der Sie den Unterschied leichter erkennen können.

Eine Blume hat eine Reihe von Dingen, die an ihr einzigartig sind. Es hat eine Colorund es hat eine Reihe von Blütenblättern (NumPetals). Wenn ich diese Blume in der realen Welt beobachte, kann ich ihre Farbe klar erkennen. Ich kann dann anhand dieser Farbe Entscheidungen treffen. Zum Beispiel, wenn die Farbe schwarz ist, geben Sie nicht an die Freundin, aber wenn die Farbe rot ist, geben Sie an die Freundin. In unserem Objektmodell wird die Farbe der Blume als Getter auf dem Blumenobjekt angezeigt. Meine Beobachtung dieser Farbe ist wichtig für Aktionen, die ich ausführen werde, hat jedoch keinerlei Einfluss auf das Blumenobjekt. Ich sollte nicht in der Lage sein, die Farbe dieser Blume zu ändern. Ebenso ist es nicht sinnvoll, die Farbeigenschaft auszublenden. Eine Blume kann Menschen im Allgemeinen nicht davon abhalten, ihre Farbe zu beobachten.

Wenn ich die Blume färbe, sollte ich die ReactToDye-Methode (Color dyeColor) für die Blume aufrufen, die die Blume gemäß den internen Regeln ändert. Anschließend kann ich die ColorEigenschaft erneut abfragen und auf Änderungen reagieren, nachdem die ReactToDye-Methode aufgerufen wurde. Es wäre falsch für mich, die Colorder Blume direkt zu modifizieren, und wenn ich könnte, dann ist die Abstraktion zusammengebrochen.

Manchmal (seltener, aber dennoch oft genug, um es zu erwähnen) sind Setter durchaus gültiges OO-Design. Wenn ich ein Kundenobjekt habe, ist es durchaus zulässig, einen Setter für seine Adresse anzugeben. Ob das nennt du setAddress(string address)oder ChangeMyAddressBecauseIMoved(string newAddress)oder einfach string Address { get; set; }eine Frage der Semantik. Der interne Status dieses Objekts muss geändert werden, und die entsprechende Methode besteht darin, den internen Status dieses Objekts festzulegen. Auch wenn ich eine historische Aufzeichnung der Adressen benötige, in denen der CustomerBenutzer gewohnt hat, kann ich den Setter verwenden, um meinen internen Status entsprechend zu ändern. In diesem Fall ist es nicht sinnvoll Customer, unveränderlich zu sein, und es gibt keinen besseren Weg, eine Adresse zu ändern, als einen Setter dafür bereitzustellen.

Ich bin nicht sicher, wer vorschlägt, dass Getters und Setter schlechte oder gegen objektorientierte Paradigmen verstoßen, aber wenn doch, tun sie dies wahrscheinlich als Reaktion auf ein bestimmtes Sprachmerkmal der Sprache, die sie gerade verwenden. Ich habe das Gefühl, dass dies aus der Java-Kultur "Alles ist ein Objekt" heraus gewachsen ist (und sich möglicherweise auf einige andere Sprachen ausgeweitet hat). Die .NET-Welt hat diese Diskussion überhaupt nicht. Getter und Setter sind eine erstklassige Sprachfunktion, die nicht nur von Benutzern verwendet wird, die Anwendungen in der Sprache schreiben, sondern auch von der Sprach-API selbst.

Sie sollten vorsichtig sein, wenn Sie Getter verwenden. Sie möchten nicht die falschen Datenelemente verfügbar machen und auch nicht die falschen Datenelemente (dh veränderbare Objekte, Verweise auf Strukturen, mit denen ein Konsument den internen Status ändern kann). Sie möchten Ihre Objekte jedoch so modellieren, dass die Daten so eingekapselt werden, dass Ihre Objekte von externen Verbrauchern verwendet und vor Missbrauch durch dieselben Verbraucher geschützt werden können. Häufig erfordert das Getter.

Zusammenfassend: Getter und Setter sind gültige objektorientierte Entwurfswerkzeuge. Sie dürfen nicht so großzügig verwendet werden, dass jedes Implementierungsdetail offengelegt wird, und Sie möchten sie auch nicht so sparsam verwenden, dass die Konsumenten eines Objekts das Objekt nicht effizient nutzen können.


-1

Was ist eine Sache ohne Meinungen, die kaputt geht, wenn ich Getter benutze?

Wartbarkeit wird brechen.

Jedes Mal (ich sollte fast immer sagen), wenn Sie einen Getter anbieten, geben Sie im Grunde mehr weg, als von Ihnen verlangt wurde. Dies bedeutet auch, dass Sie mehr warten müssen, als verlangt wurde. Sie verringern daher die Wartbarkeit ohne wirklichen Grund.

Gehen wir zum CollectionBeispiel von @ candied_orange . Im Gegensatz zu dem, was er schreibt, Collection sollte man keinen Getter haben. Sammlungen existieren aus ganz bestimmten Gründen, insbesondere, um alle Elemente zu durchlaufen . Diese Funktionalität ist für niemanden überraschend. Sie sollte daher in der implementiert werden Collection, anstatt den offensichtlichen Anwendungsfall auf den Benutzer zu übertragen, indem For-Cycle und Getter oder so weiter verwendet werden.

Um fair zu sein, einige Grenzen tun müssen Getter. Dies passiert, wenn Sie wirklich nicht wissen, wofür sie verwendet werden. Beispielsweise können Sie den Stacktrace Exceptionheutzutage programmgesteuert aus Javas Klasse abrufen, weil die Leute ihn aus allerlei merkwürdigen Gründen verwenden wollten, die sich die Autoren nicht vorstellen konnten oder wollten.


1
Gegenbeispiel. Stellen Sie sich eine Funktion vor Collection, die wie in zwei Sekunden gleichzeitig iterieren muss (A1, B1), (A2, B2), .... Dies erfordert eine Implementierung von "zip". Wie implementieren Sie "zip", wenn die Iterationsfunktion auf einem der beiden implementiert ist Collection? Wie greifen Sie auf das entsprechende Element auf dem anderen zu?
5.

@rwong Das Collectionmuss zipmit sich selbst implementieren , je nachdem wie die Bibliothek geschrieben ist. Wenn dies nicht der Fall ist, implementieren Sie es und senden eine Pull-Anfrage an den Ersteller der Bibliothek. Beachten Sie, dass ich zugestimmt habe, dass einige Grenzen manchmal Getter benötigen. Mein eigentliches Problem ist, dass Getter heutzutage überall sind.
Robert Bräutigam

In diesem Fall bedeutet das, dass die Bibliothek Getter für das CollectionObjekt haben muss, zumindest für ihre eigene Implementierung von zip. (Das heißt, der Getter muss mindestens
Paketsichtbarkeit

Ich verstehe dich nicht ganz. Das Collectionkann offensichtlich auf seinen eigenen internen Zustand zugreifen, genauer gesagt, jede CollectionInstanz kann auf den internen Zustand eines anderen zugreifen. Es ist der gleiche Typ, keine Getter erforderlich.
Robert Bräutigam

Ich höre Sie auch, aber was genau ist die bessere Lösung für Getter? Ich weiß, dass es nicht in Frage kommt.
Coolpasta
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.