Ist „Vermeide das Jojo-Problem“ ein triftiger Grund, die „primitive Obsession“ zuzulassen?


42

Laut Wann ist primitive Obsession kein Code-Geruch? Ich sollte ein ZipCode-Objekt erstellen, um eine Postleitzahl anstelle eines String-Objekts darzustellen.

Nach meiner Erfahrung ziehe ich es jedoch vor, etwas zu sehen

public class Address{
    public String zipCode;
}

Anstatt von

public class Address{
    public ZipCode zipCode;
}

weil ich denke, dass Letzteres erfordert, dass ich zur ZipCode-Klasse wechsle, um das Programm zu verstehen.

Und ich glaube, ich muss zwischen vielen Klassen wechseln, um die Definition zu sehen, wenn alle primitiven Datenfelder durch eine Klasse ersetzt wurden, die das Gefühl hat, unter dem Jojo-Problem (einem Anti-Muster) zu leiden .

Deshalb möchte ich die ZipCode-Methoden in eine neue Klasse verschieben, zum Beispiel:

Alt:

public class ZipCode{
    public boolean validate(String zipCode){
    }
}

Neu:

public class ZipCodeHelper{
    public static boolean validate(String zipCode){
    }
}

Damit nur derjenige, der die Postleitzahl validieren muss, von der ZipCodeHelper-Klasse abhängt. Und ich habe einen weiteren "Vorteil" bei der Beibehaltung der primitiven Besessenheit gefunden: Die Klasse sieht aus wie die serialisierte Form, wenn überhaupt, zum Beispiel: eine Adresstabelle mit der Zeichenfolgenspalte zipCode.

Meine Frage ist, ist "Vermeiden des Jojo-Problems" (Verschieben zwischen Klassendefinitionen) ein gültiger Grund, die "primitive Besessenheit" zuzulassen?


9
@ jpmc26 Dann wären Sie schockiert zu sehen, wie komplex unser Postleitzahl-Objekt ist - nicht, dass es richtig ist, aber es existiert
Jared Goguen

9
@ jpmc26, ich verstehe nicht, wie du von "komplex" zu "schlecht designt" kommst. Komplexer Code ist oft das Ergebnis von einfachem Code, der mit der Komplexität der realen Welt in Berührung kommt und nicht mit der idealen Welt, die wir uns wünschen könnten. "Zurück zu dieser Doppelseitenfunktion. Ja, ich weiß, es ist nur eine einfache Funktion, ein Fenster anzuzeigen, aber es hat kleine Haare und Dinge darauf gewachsen und niemand weiß warum. Nun, ich werde Ihnen sagen, warum: das sind Fehler behebt. "
Kyralessa

18
@ jpmc26 - Der Punkt beim Umschließen von Objekten wie ZipCode ist die Typensicherheit. Die Postleitzahl ist keine Zeichenfolge, sondern eine Postleitzahl. Wenn eine Funktion eine Postleitzahl erwartet, sollten Sie nur eine Postleitzahl und keine Zeichenfolge übergeben können.
Davor Ždralo

4
Das fühlt sich sehr sprachspezifisch an, verschiedene Sprachen machen hier verschiedene Dinge. @ DavorŽdralo In der gleichen Strecke sollten wir auch viele numerische Typen hinzufügen. "nur positive ganze Zahlen", "nur gerade Zahlen" können auch Typen sein.
Paul23

6
@ paul23 Ja, und der Hauptgrund, warum wir diese nicht haben, ist, dass viele Sprachen keine eleganten Arten ihrer Definition unterstützen. Es ist durchaus sinnvoll, "age" als einen anderen Typ als "temperature in degrees celsius" zu definieren, wenn nur "userAge == currentTemperature" als Unsinn erkannt wird.
IMSoP

Antworten:


116

Es wird davon ausgegangen, dass Sie nicht zur ZipCode-Klasse wechseln müssen, um die Address-Klasse zu verstehen. Wenn ZipCode gut entworfen ist, sollte es offensichtlich sein, was es tut, indem es nur die Adressenklasse liest.

Programme werden nicht durchgehend gelesen - normalerweise sind Programme viel zu komplex, um dies zu ermöglichen. Sie können nicht den gesamten Code eines Programms gleichzeitig im Kopf behalten. Wir verwenden also Abstraktionen und Kapseln, um das Programm in sinnvolle Einheiten zu unterteilen, sodass Sie sich einen Teil des Programms (z. B. die Address-Klasse) ansehen können, ohne den gesamten Code lesen zu müssen, von dem es abhängt.

Ich bin mir zum Beispiel sicher, dass Sie nicht jedes Mal den Quellcode für String lesen müssen, wenn Sie auf String im Code stoßen.

Das Umbenennen der Klasse von ZipCode in ZipCodeHelper schlägt vor, dass es jetzt zwei separate Konzepte gibt: eine Postleitzahl und eine Postleitzahlhilfe. Also doppelt so komplex. Und jetzt kann Ihnen das Typensystem nicht helfen, zwischen einer beliebigen Zeichenfolge und einer gültigen Postleitzahl zu unterscheiden, da sie den gleichen Typ haben. Hier ist "Besessenheit" angebracht: Sie schlagen eine komplexere und weniger sichere Alternative vor, nur weil Sie einen einfachen Wrapper-Typ um ein Primitiv vermeiden möchten.

Die Verwendung eines Grundelements ist meiner Meinung nach in den Fällen gerechtfertigt, in denen es keine Validierung oder andere Logik gibt, die von diesem bestimmten Typ abhängt. Sobald Sie jedoch eine Logik hinzufügen, ist es viel einfacher, wenn diese Logik mit dem Typ gekapselt ist.

Was die Serialisierung angeht, klingt dies nach einer Einschränkung des verwendeten Frameworks. Sicherlich sollten Sie in der Lage sein, einen ZipCode zu einer Zeichenfolge zu serialisieren oder einer Spalte in einer Datenbank zuzuordnen.


2
Ich stimme dem (Haupt-) Teil "sinnvolle Einheiten" zu, aber nicht so sehr, dass eine Postleitzahl und eine Postleitzahl-Validierung dasselbe Konzept sind. ZipCodeHelper(was ich eher nennen würde ZipCodeValidator) könnte sehr gut eine Verbindung zu einem Webdienst herstellen, um seine Arbeit zu erledigen. Das wäre nicht Teil der Einzelverantwortung "Halten Sie die Postleitzahl-Daten". Wenn Sie das Typsystem so einstellen, dass ungültige Postleitzahlen nicht zulässig sind, können Sie den ZipCodeKonstruktor weiterhin als Äquivalent zu Javas package-private definieren und diesen mit einem aufrufen, ZipCodeFactoryder immer den Validator aufruft.
R. Schmitz,

16
@ R.Schmitz: Das ist nicht das, was "Verantwortung" im Sinne des Grundsatzes der Einzelverantwortung bedeutet. In jedem Fall sollten Sie jedoch so viele Klassen verwenden, wie Sie benötigen, solange Sie die Postleitzahl und ihre Validierung einkapseln. Das OP schlägt einen Helfer vor, anstatt die Postleitzahl einzukapseln, was eine schlechte Idee ist.
JacquesB

1
Ich möchte respektvoll widersprechen. SRP bedeutet, dass eine Klasse "einen und nur einen Änderungsgrund" haben sollte (Änderung in "Woraus eine Postleitzahl besteht" im Vergleich zu "Wie sie validiert wird"). Auf diesen speziellen Fall wird im Buch Clean Code weiter eingegangen : " Objekte verbergen ihre Daten hinter Abstraktionen und stellen Funktionen zur Verfügung, die mit diesen Daten arbeiten. Datenstruktur stellt ihre Daten zur Verfügung und hat keine sinnvollen Funktionen. " - ZipCodewäre eine "Datenstruktur". und ZipCodeHelperein "Objekt". In jedem Fall sind wir uns einig, dass wir keine Webverbindungen an den ZipCode-Konstruktor übergeben müssen.
R. Schmitz

9
Die Verwendung eines Grundelements ist meiner Meinung nach in den Fällen gerechtfertigt, in denen es keine Validierung oder andere Logik gibt, die von diesem bestimmten Typ abhängt. => Ich stimme nicht zu. Selbst wenn alle Werte gültig sind, würde ich es vorziehen, die Semantik der Sprache zu übermitteln, anstatt Primitive zu verwenden. Wenn eine Funktion für einen primitiven Typ aufgerufen werden kann, der für seine aktuelle semantische Verwendung unsinnig ist, sollte es sich nicht um einen primitiven Typ handeln, sondern um einen geeigneten Typ, für den nur die sinnvollen Funktionen definiert sind. (Wenn Sie beispielsweise inteine ID als ID verwenden, können Sie diese mit einer ID multiplizieren ...)
Matthieu M.,

@ R.Schmitz Ich denke, Postleitzahlen sind ein schlechtes Beispiel für die Unterscheidung, die Sie machen. Etwas, das sich oft ändert, ist möglicherweise ein Kandidat für Einzelunterricht Foound FooValidatorKlassen. Wir könnten eine ZipCodeKlasse haben, die das Format ZipCodeValidatorüberprüft und einen Webservice aufruft, um zu überprüfen, ob eine korrekt formatierte ZipCodeVersion aktuell ist. Wir wissen, dass sich Postleitzahlen ändern. In der Praxis wird es jedoch eine Liste gültiger Postleitzahlen geben, die in ZipCodeoder in einer lokalen Datenbank eingekapselt sind .
Nein U

55

Wenn das geht:

new ZipCode("totally invalid zip code");

Und der Konstruktor für ZipCode macht:

ZipCodeHelper.validate("totally invalid zip code");

Dann haben Sie die Kapselung unterbrochen und der ZipCode-Klasse eine ziemlich alberne Abhängigkeit hinzugefügt. Wenn der Konstruktor nicht aufruft ZipCodeHelper.validate(...), haben Sie isolierte Logik auf seiner eigenen Insel, ohne sie tatsächlich zu erzwingen. Sie können ungültige Postleitzahlen erstellen.

Die validateMethode sollte eine statische Methode für die ZipCode-Klasse sein. Jetzt wird das Wissen über eine "gültige" Postleitzahl zusammen mit der ZipCode-Klasse gebündelt. Da Ihre Codebeispiele wie Java aussehen, sollte der Konstruktor von ZipCode eine Ausnahme auslösen, wenn ein falsches Format angegeben wird:

public class ZipCode {
    private String zipCode;

    public ZipCode(string zipCode) {
        if (!validate(zipCode))
            throw new IllegalFormatException("Invalid zip code");

        this.zipCode = zipCode;
    }

    public static bool validate(String zipCode) {
        // logic to check format
    }

    @Override
    public String toString() {
        return zipCode;
    }
}

Der Konstruktor überprüft das Format und löst eine Ausnahme aus, wodurch verhindert wird, dass ungültige Postleitzahlen erstellt werden. Die statische validateMethode steht anderen Codes zur Verfügung, sodass die Logik zum Überprüfen des Formats in der ZipCode-Klasse eingekapselt ist.

In dieser Variante der ZipCode-Klasse gibt es kein "Jo-Jo". Es heißt nur richtige objektorientierte Programmierung.


Wir werden auch die Internationalisierung hier ignorieren, was möglicherweise eine andere Klasse mit dem Namen ZipCodeFormat oder PostalService erforderlich macht (z. B. PostalService.isValidPostalCode (...), PostalService.parsePostalCode (...) usw.).


28
Hinweis: Der Hauptvorteil bei der Vorgehensweise von @Greg Burkhardt besteht darin, dass Sie darauf vertrauen können, dass ein ZipCode-Objekt eine gültige Zeichenfolge enthält, ohne es erneut überprüfen zu müssen, da der Typ und die Tatsache, dass es erfolgreich erstellt wurde, dies für Sie bedeuten diese Garantie. Wenn Sie stattdessen Zeichenfolgen weitergegeben haben, müssen Sie möglicherweise an verschiedenen Stellen in Ihrem Code "validate (zipCode)" aktivieren, um sicherzugehen, dass Sie eine gültige Postleitzahl haben. Bei einem erfolgreich erstellten ZipCode-Objekt können Sie dem jedoch vertrauen ihr Inhalt ist gültig, ohne dass sie erneut überprüft werden müssen.
Ein Typ

3
@ R.Schmitz: Die ZipCode.validateMethode ist die Vorprüfung, die durchgeführt werden kann, bevor ein Konstruktor aufgerufen wird, der eine Ausnahme auslöst .
Greg Burghardt

10
@ R.Schmitz: Wenn Sie Bedenken hinsichtlich einer ärgerlichen Ausnahme haben, können Sie den ZipCode-Konstruktor auch als privat kennzeichnen und eine öffentliche statische Factory-Funktion (Zipcode.create?) Bereitstellen, mit der die übergebenen Parameter überprüft werden. Gibt null zurück, wenn dies nicht erfolgreich ist, und erstellt andernfalls ein ZipCode-Objekt und gibt es zurück. Der Aufrufer muss natürlich immer nach einem Null-Rückgabewert suchen. Auf der anderen Seite ist die Ausnahme in der Praxis möglicherweise nicht so ärgerlich, wenn Sie es sich beispielsweise zur Gewohnheit machen, vor dem Erstellen eines ZipCodes immer zu validieren (regex? Validate? Etc.).
Ein Typ

11
Eine Factory-Funktion, die einen optionalen <ZipCode> zurückgibt, ist ebenfalls möglich. Dann hat der Anrufer keine andere Wahl, als einen möglichen Ausfall der Werksfunktion explizit zu behandeln. Unabhängig davon wird der Fehler nicht viel später, sondern irgendwo in der Nähe des Ortes entdeckt, an dem er erstellt wurde, und zwar durch Client-Code, der weit vom ursprünglichen Problem entfernt ist.
Ein

6
Sie können ZipCode nicht unabhängig validieren, also nicht. Sie benötigen das Country-Objekt wirklich, um die Regeln für die Postleitzahl- / PostCode-Überprüfung nachzuschlagen.
Joshua

11

Wenn Sie sich viel mit dieser Frage auseinandersetzen, ist die von Ihnen verwendete Sprache möglicherweise nicht das richtige Werkzeug für den Job? Diese Art von "domänentypisierten Primitiven" ist trivial einfach in beispielsweise F # auszudrücken.

Dort könnten Sie zum Beispiel schreiben:

type ZipCode = ZipCode of string
type Town = Town of string

type Adress = {
  zipCode: ZipCode
  town: Town
  //etc
}

let adress1 = {
  zipCode = ZipCode "90210"
  town = Town "Beverly Hills"
}

let faultyAdress = {
  zipCode = "12345"  // <-Compiler error
  town = adress1.zipCode // <- Compiler error
}

Dies ist sehr nützlich, um häufige Fehler zu vermeiden, z. B. den Vergleich von IDs verschiedener Entitäten. Und da diese typisierten Grundelemente viel leichter sind als eine C # - oder Java-Klasse, werden Sie sie letztendlich tatsächlich verwenden.


Interessant - wie würde es aussehen, wenn Sie die Validierung erzwingen wollen ZipCode?
Hulk

4
@Hulk Sie können OO-Stil in F # schreiben und die Typen in Klassen umwandeln. Ich bevorzuge jedoch den funktionalen Stil, indem ich den Typ mit ZipCode = private ZipCode of string deklariere und ein ZipCode-Modul mit einer create-Funktion hinzufüge. Hier einige Beispiele: gist.github.com/swlaschin/54cfff886669ccab895a
Guran

@ Bent-Tranberg Danke für die Bearbeitung. Sie haben Recht, eine einfache Typabkürzung bietet keine Sicherheit für den Typ der Kompilierungszeit.
Guran

Wenn Ihnen mein erster Kommentar zugesandt wurde, den ich gelöscht habe, war der Grund, dass ich Ihre Quelle zum ersten Mal missverstanden habe. Ich habe es nicht sorgfältig genug gelesen. Als ich versuchte, es zu kompilieren, wurde mir schließlich klar, dass Sie genau dies demonstrieren wollten, und ich entschied mich, es zu bearbeiten, um es richtig zu machen.
Bent Tranberg

Ja. Meine ursprüngliche Quelle war in Ordnung, leider einschließlich des angeblich ungültigen Beispiels. Doh! Sollte nur mit Wlaschin verlinkt sein, anstatt selbst Code einzugeben
Guran

6

Die Antwort hängt ganz davon ab, was Sie tatsächlich mit den Postleitzahlen machen möchten. Hier sind zwei extreme Möglichkeiten:

(1) Alle Adressen befinden sich garantiert in einem einzelnen Land. Überhaupt keine Ausnahmen. (ZB keine ausländischen Kunden oder keine Mitarbeiter, deren Privatadresse sich im Ausland befindet, während sie für einen ausländischen Kunden arbeiten.) In diesem Land gibt es Postleitzahlen, von denen zu erwarten ist, dass sie niemals ernsthafte Probleme verursachen (dh sie erfordern keine Freiform-Eingabe) wie "derzeit D4B 6N2, dies ändert sich jedoch alle 2 Wochen"). Die Postleitzahlen werden nicht nur zur Adressierung, sondern auch zur Validierung von Zahlungsinformationen oder ähnlichen Zwecken verwendet. - Unter diesen Umständen ist eine Postleitzahlklasse sehr sinnvoll.

(2) Adressen können sich in fast jedem Land befinden, daher sind Dutzende oder Hunderte von Adressierungsschemata mit oder ohne Postleitzahl (und mit Tausenden von Ausnahmen und Sonderfällen) relevant. Eine "Postleitzahl" wird eigentlich nur benötigt, um Menschen aus Ländern, in denen Postleitzahlen verwendet werden, daran zu erinnern, dass sie vergessen haben, ihre Postleitzahl anzugeben. Die Adressen werden nur verwendet, damit der Zugriff wiederhergestellt wird, wenn jemand den Zugriff auf sein Konto verliert und seinen Namen und seine Adresse nachweisen kann. - Unter diesen Umständen wären Postleitzahlklassen für alle relevanten Länder ein enormer Aufwand. Zum Glück werden sie gar nicht benötigt.


3

In den anderen Antworten wurde über die Modellierung von OO-Domänen und die Verwendung eines umfassenderen Typs zur Darstellung Ihres Werts gesprochen.

Ich bin nicht anderer Meinung, insbesondere angesichts des von Ihnen geposteten Beispielcodes.

Aber ich frage mich auch, ob das tatsächlich den Titel Ihrer Frage beantwortet.

Stellen Sie sich das folgende Szenario vor (aus einem aktuellen Projekt, an dem ich arbeite):

Sie haben eine Remote-Anwendung auf einem Feldgerät, die mit Ihrem zentralen Server kommuniziert. Eines der DB-Felder für den Geräteeintrag ist eine Postleitzahl für die Adresse, unter der sich das Feldgerät befindet. Sie interessieren sich nicht für die Postleitzahl (oder den Rest der Adresse für diese Angelegenheit). Alle Menschen, die sich dafür interessieren, befinden sich auf der anderen Seite einer HTTP-Grenze: Sie sind einfach die einzige Quelle der Wahrheit für die Daten. Es hat keinen Platz in Ihrer Domain-Modellierung. Sie zeichnen es einfach auf, validieren es, speichern es und mischen es auf Anfrage in einem JSON-Blob zu anderen Stellen.

In diesem Szenario ist es wahrscheinlich ein Overkill der YAGNI-Variante , weit über die Validierung der Einfügung mit einer SQL-Regex-Einschränkung (oder ihrer ORM-Entsprechung) hinauszugehen.


6
Ihre SQL-Regex-Einschränkung kann als qualifizierter Typ angesehen werden. In Ihrer Datenbank wird die Postleitzahl nicht als "VarChar", sondern als "VarChar, eingeschränkt durch diese Regel" gespeichert. In einigen DBMS können Sie dieser Einschränkung vom Typ + leicht einen Namen als wiederverwendbaren "Domänentyp" geben, und wir befinden uns wieder an der empfohlenen Stelle, um den Daten einen aussagekräftigen Typ zu geben. Ich stimme Ihrer Antwort im Prinzip zu, denke aber nicht, dass dieses Beispiel passt. Ein besseres Beispiel wäre, wenn Ihre Daten "unformatierte Sensordaten" sind und der aussagekräftigste Typ "Byte-Array" ist, da Sie keine Ahnung haben, was die Daten bedeuten.
IMSoP

@IMSoP interessanter Punkt. Ich bin mir allerdings nicht sicher, ob ich dem zustimme: Sie könnten eine Postleitzahl in Java (oder einer anderen Sprache) mit einem regulären Ausdruck validieren, sie jedoch weiterhin als Zeichenfolge anstelle eines reichhaltigeren Typs behandeln. Abhängig von der Domänenlogik kann eine weitere Manipulation erforderlich sein (z. B. um sicherzustellen, dass die Postleitzahl mit dem Bundesstaat übereinstimmt, was mit regulären Ausdrücken schwierig / unmöglich zu überprüfen wäre).
Jared Smith

Sie könnten , aber sobald Sie dies tun, schreiben Sie ihm ein domänenspezifisches Verhalten zu, und genau das, was in den zitierten Artikeln steht, sollte zur Erstellung eines benutzerdefinierten Typs führen. Die Frage ist nicht, ob Sie es auf die eine oder die andere Art tun können , sondern ob Sie es sollten , vorausgesetzt, Ihre Programmiersprache gibt Ihnen die Wahl.
IMSoP

Sie können so etwas wie ein Modell RegexValidatedStringerstellen, das die Zeichenfolge selbst und den zur Validierung verwendeten regulären Ausdruck enthält. Aber es sei denn, jede Instanz hat eine eindeutige reguläre Ausdrücke (was möglich, aber unwahrscheinlich ist), scheint dies ein bisschen albern und speicherverschwendend (und möglicherweise die Kompilierungszeit für reguläre Ausdrücke). Sie legen den regulären Ausdruck entweder in eine separate Tabelle und hinterlassen in jedem Fall einen Nachschlageschlüssel, um ihn zu finden (was aufgrund der Indirektion wahrscheinlich schlimmer ist), oder Sie finden eine Möglichkeit, ihn einmal für jeden gemeinsamen Werttyp zu speichern, der diese Regel teilt. - z.B. ein statisches Feld für einen Domänentyp oder eine äquivalente Methode, wie IMSoP sagte.
Miral

1

Die ZipCodeAbstraktion kann nur sinnvoll sein, wenn Ihre AddressKlasse keine TownNameEigenschaft hat. Ansonsten haben Sie eine halbe Abstraktion: Die Postleitzahl bezeichnet die Stadt, aber diese beiden verwandten Informationen befinden sich in verschiedenen Klassen. Das ergibt keinen Sinn.

Aber selbst dann ist es immer noch keine richtige Anwendung (oder vielmehr Lösung für) primitive Obsession. Das konzentriert sich meines Wissens hauptsächlich auf zwei Dinge:

  1. Verwenden von Primitiven als Eingabe- (oder sogar Ausgabewerte) einer Methode, insbesondere wenn eine Sammlung von Primitiven benötigt wird.
  2. Klassen, die mit der Zeit zusätzliche Eigenschaften erhalten, ohne jemals zu überdenken, ob einige davon in eine eigene Unterklasse gruppiert werden sollen.

Ihr Fall ist keiner. Eine Adresse ist ein genau definiertes Konzept mit eindeutig erforderlichen Eigenschaften (Straße, Hausnummer, Postleitzahl, Ort, Bundesland, Land, ...). Es gibt kaum einen Grund, diese Daten aufzulösen, da sie eine einzige Verantwortung haben: einen Ort auf der Erde zu bestimmen. Eine Adresse benötigt alle diese Felder, um aussagekräftig zu sein. Eine halbe Adresse ist sinnlos.

So wissen Sie, dass Sie nicht weiter unterteilen müssen: Eine weitere Zerlegung würde die funktionale Absicht der AddressKlasse beeinträchtigen . Ebenso brauchen Sie keine NameUnterklasse, um in der PersonKlasse verwendet zu werden, es sei denn, Name(ohne eine Person im Anhang) ist ein sinnvolles Konzept in Ihrer Domäne. Was es (normalerweise) nicht ist. Namen werden zur Identifizierung von Personen verwendet, sie haben normalerweise keinen eigenen Wert.


1
@RikD: Aus der Antwort: "Sie brauchen keine NameUnterklasse, um in der PersonKlasse verwendet zu werden, es sei denn, Name (ohne Person) ist ein sinnvolles Konzept in Ihrer Domain ." Wenn Sie eine benutzerdefinierte Überprüfung für die Namen vorgenommen haben, ist der Name zu einem aussagekräftigen Konzept in Ihrer Domain geworden. was ich ausdrücklich als gültigen Anwendungsfall für die Verwendung eines Subtyps erwähnt habe. Zweitens führen Sie für die Postleitzahlvalidierung zusätzliche Annahmen ein, z. B. Postleitzahlen, die dem Format eines bestimmten Landes entsprechen müssen. Sie sprechen ein Thema an, das viel breiter ist als die Absicht der OP-Frage.
Flater

5
" Eine Adresse ist ein genau definiertes Konzept mit eindeutig erforderlichen Eigenschaften (Straße, Hausnummer, Postleitzahl, Ort, Bundesland, Land). " - Nun, das ist einfach falsch. Informationen dazu finden Sie im Adressformular von Amazon.
R. Schmitz

4
@Flater Nun, ich werde Ihnen nicht die Schuld geben, dass Sie nicht die vollständige Liste der Lügen gelesen haben, weil sie ziemlich lang ist, aber sie enthält wörtlich "Adressen haben eine Straße", "Eine Adresse erfordert sowohl eine Stadt als auch ein Land", "Eine Adresse wird eine Postleitzahl usw. haben, was im Gegensatz zu dem steht, was der zitierte Satz sagt.
R. Schmitz

8
@GregBurghardt "Die Postleitzahl setzt den United States Postal Service voraus, und Sie können den Namen der Stadt aus der Postleitzahl ableiten. Städte können mehrere Postleitzahlen haben, aber jede Postleitzahl ist nur an eine Stadt gebunden." Dies ist im Allgemeinen nicht korrekt. Ich habe eine Postleitzahl, die hauptsächlich für eine Nachbarstadt verwendet wird, aber meine Residenz befindet sich nicht dort. Postleitzahlen stimmen nicht immer mit Regierungsgrenzen überein. Zum Beispiel enthält 42223 Landkreise von TN und KY .
JimmyJames

2
In Österreich gibt es ein Tal, das nur von Deutschland aus zugänglich ist ( en.wikipedia.org/wiki/Kleinwalsertal ). Für diese Region besteht ein Sondervertrag, der unter anderem auch die Angabe der österreichischen und deutschen Postleitzahlen für Adressen in diesem Gebiet beinhaltet. Im Allgemeinen kann man nicht einmal davon ausgehen, dass eine Adresse nur eine einzige gültige Postleitzahl hat;)
Hulk

1

Aus dem Artikel:

Allgemeiner kann sich das Jojo-Problem auch auf jede Situation beziehen, in der eine Person ständig zwischen verschiedenen Informationsquellen wechseln muss, um ein Konzept zu verstehen.

Quellcode wird weitaus häufiger gelesen als geschrieben. Daher ist das Jojo-Problem, zwischen vielen Dateien wechseln zu müssen, ein Problem.

Allerdings nicht , das Jo-Jo - Problem fühlt sich viel mehr relevant , wenn sie mit tief voneinander abhängigen Module oder Klassen (die hin und her zwischen ihnen nennen) zu tun. Das ist ein Albtraum der besonderen Art, den es zu lesen gilt, und wahrscheinlich hatte der Geber des Jojo-Problems das im Sinn.

Aber - ja , es ist wichtig, zu viele Abstraktionsebenen zu vermeiden!

Alle nicht-trivialen Abstraktionen sind bis zu einem gewissen Grad undicht. - das Gesetz der undichten Abstraktionen.

Ich bin zum Beispiel nicht einverstanden mit der Annahme in der Antwort von mmmaaa, dass "Sie kein Jo-Jo benötigen, um die ZipCode-Klasse zu [(besuchen)], um die Address-Klasse zu verstehen". Ich habe die Erfahrung gemacht, dass Sie dies tun - zumindest die ersten Male, wenn Sie den Code lesen. Wie andere angemerkt haben, gibt es jedoch Zeiten, in denen eine ZipCodeKlasse angemessen ist.

YAGNI (Ya wird es nicht brauchen) ist ein besseres Muster, um Lasagne-Code (Code mit zu vielen Ebenen) zu vermeiden. Abstraktionen wie Typen und Klassen dienen dem Programmierer und sollten nicht verwendet werden, es sei denn, sie sind vorhanden eine Hilfe.

Ich persönlich möchte "Codezeilen speichern" (und natürlich die zugehörigen "Dateien / Module / Klassen speichern" usw.). Ich bin zuversichtlich, dass es einige gibt, die mir den Beinamen "primitiv besessen" geben würden - ich finde es wichtiger, Code zu haben, über den man leicht nachdenken kann, als sich um Etiketten, Muster und Anti-Muster zu kümmern. Die richtige Wahl, wann eine Funktion, ein Modul / eine Datei / eine Klasse oder eine Funktion an einem gemeinsamen Ort erstellt werden soll, ist sehr situationsabhängig. Ich strebe in etwa 3-100 Zeilenfunktionen, 80-500 Zeilendateien und "1, 2, n" für wiederverwendbaren Bibliothekscode an ( SLOC - ohne Kommentare oder Boilerplate; ich möchte in der Regel mindestens 1 zusätzliches SLOC-Minimum pro obligatorischer Zeile Kochplatte).

Die meisten positiven Muster sind von Entwicklern entstanden, die genau das taten, wann sie es brauchten . Es ist viel wichtiger zu lernen, wie man lesbaren Code schreibt, als zu versuchen, Muster anzuwenden, ohne dass das gleiche Problem zu lösen ist. Jeder gute Entwickler kann das Factory-Muster implementieren, ohne es zuvor gesehen zu haben, in dem seltenen Fall, dass es für sein Problem die richtige Lösung ist. Ich habe das Factory-Muster, das Beobachter-Muster und wahrscheinlich Hunderte davon verwendet, ohne ihren Namen zu kennen (dh gibt es ein "variables Zuweisungsmuster"?). Für ein unterhaltsames Experiment - sehen Sie, wie viele GoF-Muster in die JS- Sprache integriert sind - habe ich 2009 nach ca. 12-15 aufgehört zu zählen. Das Factory-Muster ist so einfach wie das Zurückgeben eines Objekts aus einem JS-Konstruktor - es ist nicht erforderlich eine WidgetFactory.

Also - ja , manchmal ZipCode ist eine gute Klasse. Jedoch nicht , ist die Jo-Jo - Problem nicht unbedingt relevant.


0

Das Jojo-Problem ist nur relevant, wenn Sie hin und her drehen müssen. Das wird durch ein oder zwei Dinge verursacht (manchmal beides):

  1. Schlechte Benennung. ZipCode scheint in Ordnung zu sein, ZoneImprovementPlanCode wird von den meisten Leuten (und den wenigen, die nicht beeindruckt sein werden) einen Blick erfordern.
  2. Unangemessene Kupplung. Angenommen, Ihre ZipCode-Klasse verfügt über eine Vorwahlsuche. Sie mögen denken, dass es sinnvoll ist, weil es praktisch ist, aber es hat nichts mit dem ZipCode zu tun, und wenn Sie es einschreiben, wissen die Leute jetzt nicht, wohin sie gehen sollen.

Wenn Sie sich den Namen ansehen und eine vernünftige Vorstellung davon haben, was er tut, und die Methoden und Eigenschaften einigermaßen offensichtliche Dinge tun, müssen Sie sich den Code nicht ansehen, sondern können ihn einfach verwenden. Das ist in erster Linie der springende Punkt bei Klassen - es handelt sich um modulare Codeteile, die isoliert verwendet und entwickelt werden können. Wenn Sie sich etwas anderes als die API ansehen müssen, um zu sehen, was die Klasse tut, ist dies bestenfalls ein teilweiser Fehler.


-1

Denken Sie daran, es gibt keine Silberkugel. Wenn Sie eine extrem einfache App schreiben, die schnell durchsucht werden muss, kann eine einfache Zeichenfolge die Aufgabe übernehmen. In 98% der Fälle würde jedoch ein Wertobjekt, wie es von Eric Evans in DDD beschrieben wurde, perfekt passen. Sie können alle Vorteile, die Value-Objekte bieten, leicht erkennen, wenn Sie sich umsehen.

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.