Vermeiden Sie Getter und Setter, indem Sie Benutzerinformationen anzeigen

10

Hintergrund

Ich lese das "Clean Code Book" und arbeite parallel dazu an Calisthenic-Objekten wie dem Bankkonto, und ich halte mich an diese Regel:

Die 9. Regel von Calisthenic-Objekten ist, dass wir keine Getter oder Setter verwenden.

Es scheint ziemlich lustig zu sein und ich stimme diesem Prinzip zu. Darüber hinaus erklärt der Autor auf Seite 98-99 von Clean Code, dass Getter / Setter die Abstraktion brechen und dass wir unser Objekt nicht fragen müssen, sondern unser Objekt mitteilen müssen.

Das macht für mich vollkommen Sinn und ich stimme diesem Prinzip voll und ganz zu. Das Problem kommt in der Praxis.

Kontext

Zum Beispiel habe ich eine Anwendung, in der ich einige Benutzer auflisten und die Benutzerdetails anzeigen muss.

Mein Benutzer besteht aus:

-> Name
   --> Firstname --> String
   --> Lastname --> String
-> PostalAddress
   --> Street --> String
   --> PostalCode --> String

Problem

Wie kann ich vorgehen oder was kann ich tun, um Fehler zu vermeiden, wenn ich nur einfache Informationen anzeigen muss ( und bestätigen muss, dass ich für dieses bestimmte Feld keine zusätzliche Operation benötige ), um den Wert für den Vornamen in einem einfachen ( zufällige) Ausgabeunterstützung?

Was fällt mir ein?

Eine Lösung besteht darin, Folgendes zu machen:

user.getName().getFirstName().getStringValue()

Was absolut schrecklich ist, viele Regeln für Calisthenic-Objekte zu brechen und das Demeter-Gesetz zu brechen.

Ein anderer wäre so etwas wie:

String firstName = user.provideFirstnameForOutput();
// That would have called in the user object =>
String firstName = name.provideFirstnameForOutput();
// That would have called in the name object =>
String firstName = firstname.provideFirstnameForOutput();

Aber ich fühle mich mit dieser Lösung nicht wohl, die nur ein "Accessor höherer Ordnung" zu sein scheint, wie das Umgehen von Standard-Getter / Setter mit einer Methode, die nur darauf abzielt, dem Demeter-Gesetz zu entsprechen ...

Irgendeine Idee ?

mfrachet
quelle

Antworten:

17

Das verbreitete Missverständnis über die Idee, Getter und Setter zu vermeiden, besteht darin, sie überall zu vermeiden, was unmöglich ist, wenn Sie an die Oberfläche Ihrer Architektur kommen und mit dem Benutzer interagieren.

Sie sollten die Verwendung von Gettern und Setzern im Geschäftslogik-Teil Ihrer Anwendung vermeiden, in dem Objekte mithilfe von Aggregaten geschützt werden sollten, die den Kontext bereitstellen, und Methoden als Befehle fungieren sollten.

Um Getter und Setter zu vermeiden, könnten Sie eine Lösung entwerfen, die der reaktiven Programmierung ähnelt und das Berichtssystem durch beobachtbare Entitäten implementiert. Dies verkompliziert jedoch die Architektur erheblich und macht auf CRUD-Ebene Ihrer Anwendung keinen Sinn, obwohl das Design für Benutzeroberflächen wirklich gut ist.

Ob Sie die Verwendung von Gettern / Setzern in Betracht ziehen sollten, hängt ganz von dem Teil der Anwendung ab, an dem Sie gerade arbeiten. Wenn Ihr Anliegen die Benutzeroberfläche ist, die Getter verwendet, ist dies für die Geschäftslogik völlig in Ordnung, bei der Sie einen Teil eines Codes basierend auf einem Wert ausführen würden, der über einen Getter abgerufen wird, nicht so sehr (dies schreit, dass die Logik tatsächlich darin gekapselt sein sollte die Klasse, in der Sie den Getter anrufen).

Beim Demeter-Gesetz geht es auch nicht darum, Punkte zu zählen, sondern lediglich darum, eine kontextgebende Klasse auseinander zu reißen, indem Getter für die Klasse verwendet werden, um ihre Komponenten zu erhalten, auf die Sie keinen eigenen Zugriff haben sollten.

Wenn Sie der Meinung sind, dass Ihre Benutzeroberfläche zu tief in Ihre Geschäftsmodelle eindringt, können Sie darüber nachdenken, Ansichtsmodelle in Ihr System einzuführen, um bestimmte Teile von Modellen in visuelle Darstellungen umzuwandeln.

Andy
quelle
Okay, ich denke an diesen Vorschlag wie an einen Mapper zwischen meiner Domain-Entität und einem benutzerdefinierten DTO für meine Präsentationsschicht, der einige Accessoren haben würde, oder?
Mfrachet
@ Margin Ziemlich genau, ja.
Andy
All dieses kaskadierende Zeug klingt wirklich gut und
flüssig
2

Eine Richtung, über die man nachdenken sollte, wäre, eine generische Elementfunktion zur Zeichenfolgenformatierung bereitzustellen, anstatt Zugriff auf die Rohdaten zu gewähren. Etwas wie das:

String lastName = user.formatDescription("$(Lastname)");
String fullName = user.formatDescription("$(Lastname), $(Firstname)");
String abbreviated = user.formatDescription("$(FnAbb). $(LnAbb).");

Sie sehen, mit diesem Ansatz sind Sie nicht nur auf die Bereitstellung der vollständigen Daten beschränkt, sondern können auch Mittel bereitstellen, um die Daten auf bequeme und sinnvolle Weise zu transformieren. Beispielsweise müssen Sie möglicherweise Streiche mit der Groß- / Kleinschreibung spielen:

String accountName = user.formatDescription("$(firstname).$(lastname)");

Sie können auch einige häufig verwendete, möglicherweise komplexe Formate einmal definieren, so könnten Sie beispielsweise sagen

String fullAddress = user.formatDescription(User.kAddressFormat);

Das Schöne an diesem Ansatz ist, dass die einzelnen Zeichenfolgen innerhalb der UserKlasse bleiben und der aufrufende Code tatsächlich wesentlich mehr Funktionen bietet. Der Nachteil ist jedoch, dass Sie den Vorlagenmechanismus implementieren müssen, in formatDescription()dem sich einige Codezeilen befinden.

Als solches könnte dies ein völliger Overkill sein: Vergessen Sie niemals, dass Programmierprinzipien nur Richtlinien sind. Und wenn das Befolgen eines anderen Prinzips gegen das KISS-Prinzip verstößt, ist es wahrscheinlich am besten, es einfach zu machen. Wenn Sie also nicht mindestens ein solches Formatierungselement benötigen, würde ich es der Einfachheit halber nicht implementieren, indem ich den Accessor-basierten Ansatz verwende.

cmaster - Monica wieder einsetzen
quelle
5
Dies scheint mir jedoch eine SRP-Verletzung zu sein. Ist es wirklich die Verantwortung eines UserObjekts, eine Vorlagensprache zu analysieren und zu kompilieren / zu interpretieren?
Jörg W Mittag
@ JörgWMittag Nicht unbedingt. Wie gesagt, das KISS-Prinzip hat Vorrang. Und ich sagte nirgends, dass die UserKlasse diese Funktionalität selbst implementieren muss. Wenn ich nur zwei Klassen hätte, die solche Funktionen benötigen, könnten Sie darauf wetten, dass ich den Mechanismus zum Ersetzen von Vorlagen in eine eigene Klasse zerlege. Dies soll ein Beispiel dafür sein, wie Sie die Abstraktion, die bereitgestellt wird, Userauf eine Ebene heben können, auf der es sich tatsächlich um mehr als nur einen Datencontainer handelt. Und ich glaube, darum geht es beim Vermeiden von Accessoren: OOP zu machen, anstatt mit ein paar structs umzugehen .
cmaster - wieder einsetzen Monica
Sie wissen, dass KISS völlig subjektiv und SRP-objektiv ist? KISS sagt Ihnen nichts darüber, was zu tun ist. Und was für Sie "einfach" ist, mag für mich nicht "einfach" sein. Ich sehe einen echten Qualitätsunterschied, wenn ich mit KISS oder SRP streite.
oopexpert