Ich versuche, einige Techniken zu kombinieren.
Es scheint eine gute Übung zu sein, niemals ein ValueObject zu erstellen, das nicht gültig ist. Der ValueObject-Konstruktor sollte daher fehlschlagen, wenn der bereitgestellte Inhalt nicht gut genug ist, um ein gültiges ValueObject zu erstellen. In den Beispielen, die ich habe, kann ein EmailAddress-Objekt nur erstellt werden, wenn ein Wert vorhanden ist. So weit, so gut.
Wenn ich den Wert der angegebenen E-Mail-Adresse überprüfe, beginne ich, an den Prinzipien zu zweifeln. Ich habe vier Beispiele, aber ich kann nicht sagen, welches als die beste Praxis angesehen werden sollte.
Beispiel 1 ist das einfache: einfach eine Konstruktfunktion, ein erforderlicher Parameter "value" und eine separate Funktion validieren, um den Code sauber zu halten. Der gesamte Validierungscode bleibt innerhalb der Klasse und wird niemals für die Außenwelt verfügbar sein. Die Klasse hat nur einen Zweck: Speichere die E-Mail-Adresse und vergewissere dich, dass sie niemals ungültig ist. Aber der Code wird niemals wiederverwendbar sein - ich erschaffe ein Objekt damit, aber das ist alles.
%Vor%Beispiel 2 macht die Validierungsfunktion zu einer statischen Funktion. Die Funktion wird niemals den Status der Klasse ändern, daher ist es eine korrekte Verwendung des statischen Schlüsselworts, und der darin enthaltene Code wird niemals in der Lage sein, irgendetwas zu irgendeiner Instanz zu ändern, die aus der Klasse erzeugt wird, die die statische Funktion einbettet. Aber wenn ich den Code wiederverwenden möchte, kann ich die statische Funktion aufrufen. Trotzdem fühlt sich das für mich dreckig an.
%Vor%Beispiel 3 führt eine andere Klasse ein, die innerhalb des Körpers meines Objekts fest codiert ist. Die andere Klasse ist eine Validierungsklasse, die den Validierungscode enthält, und erstellt somit eine Klasse, die immer und überall verwendet werden kann, wo ich eine Validierungsklasse benötige. Die Klasse selbst ist fest codiert, was auch bedeutet, dass ich eine Abhängigkeit von dieser Validierungsklasse erzeuge, die immer in der Nähe sein sollte und nicht durch die Abhängigkeitsinjektion injiziert wird. Man könnte sagen, dass ein fest codierter Validator genauso schlecht ist wie der vollständige Code im Objekt, aber andererseits: DI ist wichtig, und auf diese Weise muss man eine neue Klasse erstellen (erweitern oder einfach umschreiben) Ändere einfach die Abhängigkeit.
%Vor%Beispiel 4 verwendet die validator-Klasse erneut, setzt sie jedoch in den Konstruktor. Mein ValueObject benötigt daher eine Validator-Klasse, die bereits vorhanden und erstellt ist, bevor die Klasse erstellt wird, aber es ist möglich, den Validator einfach zu überschreiben. Aber wie gut ist es für eine einfache ValueObject-Klasse, eine solche Abhängigkeit im Konstruktor zu haben, da die einzige Sache, die wirklich wichtig ist, der Wert ist, sollte es nicht meine Sorge sein, zu wissen, wie und wo zu handhaben ist, wenn die E-Mail korrekt ist ein korrekter Validator.
%Vor%Das letzte Beispiel, über das ich nachgedacht habe, stellt einen Standard-Validator dar und ermöglicht es mittlerweile, über DI ein Überschreiben für den Validator im Konstruktor zu injizieren. Aber ich begann zu zweifeln, wie gut ein einfaches ValueObject ist, wenn man den wichtigsten Teil überschreibt: die Validierung.
Also, jeder hat eine Antwort, wie man diese Klasse am besten schreiben sollte, das ist richtig für etwas so einfaches wie eine E-Mail-Adresse, oder etwas Komplexeres wie ein Barcode oder eine Visa-Karte oder was auch immer man denken mag. t verletzen DDD, DI, OOP, TROCKEN, falsche Verwendung von statisch, und so weiter ...
Der vollständige Code:
%Vor%Beispiel 4!
Warum? Weil es einfach und überprüfbar ist.
Abhängig davon, was Ihr Validator tatsächlich tut (unter bestimmten Umständen kann sich Ihr Validierer auf einen API-Aufruf oder einen Aufruf einer Datenbank verlassen), ist der injizierbare Validator vollständig über Mocks testbar. Alle anderen sind entweder unmöglich unter den Umständen, die ich gerade erwähnt habe, oder unglaublich schwer zu testen.
BEARBEITEN: Für diejenigen, die sich fragen, wie die Methode der Abhängigkeitsinjektion beim Testen hilft, dann betrachten Sie die CommentValidator-Klasse, die eine Standard-Akismet-Spam-Prüfbibliothek verwendet.
%Vor%Und jetzt unten, wenn Sie Ihre Komponententests einrichten, haben wir eine CommentValidatorMock-Klasse, die wir stattdessen verwenden, wir haben Setter, um die 2 Ausgabebools, die wir haben können, manuell zu ändern, und wir haben die 2 Funktionen von oben. d, um auszugeben, was auch immer wir wollen, ohne die Akismet-API durchlaufen zu müssen.
%Vor%Wenn Sie Unit-Tests ernst nehmen, müssen Sie DI verwenden.
Der erste Instinkt ist normalerweise der beste. Sie sollten die erste Option verwenden. EmailAddress ist ein Wertobjekt. Es kann in anderen Wertobjekten oder Entitäten wiederverwendet werden. Ich verstehe nicht, warum du denkst, dass es nicht wiederverwendbar ist. Sie können eine "gemeinsame Bibliothek" dieser gemeinsamen Wertobjekte haben, die in anderen beschränkten Kontexten verwendet werden. Sei einfach vorsichtig, was du da reingibst. Sie müssten wirklich generisch sein, wenn das überhaupt möglich wäre.
Tags und Links php dependency-injection validation domain-driven-design value-objects