Artikelformat

ValueObjects (Teil 2)

letzte Woche haben wir ValueObjects kennen gelernt und auch die Möglickeit eines Object-Pools gesehen. Jetzt geht es um einen weiteren Punkt, die Erzeugung valider Objekte. Schließlich kann das Person-Objekt aus der letzten Woche durchaus als Vor- und Nachname null haben. Um daraus entstehende Probleme zu vermeiden, passen wir das ValueObject heute noch etwas an.

Ein VO muss von vorneherein valide sein. Genau wie wir von einer 2 erwarten, dass sie valide ist. Hierfür erweitern wir das VO von letzter Woche um eine isValid Methode. Diese Methode überpüft die Eingabe und nur dann wir das VO erzeugt. Damit man den Fehlerfall sauber behandeln kann, wird eine Exception geworfen.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class IllegalArgumentException extends Exception {}
 
final class Person {
 
    private $firstname;
    private $lastname;
    private static $pool = array();
 
    private function __construct($firstname, $lastname) {
        $this->firstname = $firstname;
        $this->lastname = $lastname;
    }
 
    public static function valueOf($firstname, $lastname) {
        if (!self::isValid($firstname, $lastname)){
            throw new IllegalArgumentException();
        }
 
        $key = md5($firstname . '_' . $lastname);
        if (!array_key_exists($key, self::$pool)) {
            self::$pool[$key] = new self($firstname, $lastname);
        }
        return self::$pool[$key];
    }
 
    private static function isValid($firstname, $lastname) {
        return is_string($firstname) && is_string($lastname);
    }
 
    public function getFirstname() {
        return $this->firstname;
    }
 
    public function getLastName() {
        return $this->lastname;
    }
 
}

Nutzt man in einem VO ein weiteres VO, bspw. die Adresse, die zur Person passt, so kann man in der isValid-Methode überprüfen, ob das entsprechende Objekt nicht null ist und auch von der richtigen Klasse ist. So erhält man eine konsistente Abbildung der fachlichen Domäne.

23 Kommentare

  1. Neue Objekte können nur mittels der valueOf-Methode erzeugt werden; da dort vor der Erzeugung validiert wird und ein VO nicht verändert werden kann, ist die Validität der Daten gegeben.

    Antworten
  2. René Woltmann

    15/03/2011 @ 20:55

    Wenn man ein „VO in einem VO“ hat, kann man doch das isValid dieses VOs aufrufen. Somit validiert sich jedes VO und dessen Klassenmember selbst.

    Antworten
  3. passiert doch hier.. da der konstruktor des VO private deklariert ist lässt sich das VO auch nur über die statische valueOf methode erzeugen.

    Antworten
  4. Nutze ich doch die Gelegenheit mal: Warum ist die „isValid“ static? Es will sich mir bis Heute nicht erschließen wann und warum ich statische Methoden verwenden soll?

    Antworten
    • Naja, die Methode wird in einem Kontext aufgerufen, in dem noch keine Instanz der Klasse existiert. Somit muss sie statisch sein. Alternativ ginge natürlich auch die Prüfung nach der Erstellung der Instanz (bspw im Konstructor) durchzuführen. So generiert man aber kein objekt, wenn es nicht notwendig ist. Warum valueOf statisch ist, ist dir dicher klar.

  5. Ich finde den Methodennamen „valueOf“ extrem verwirrend. Der macht doch keinen Sinn für eine Methode, die ein Objekt erzeugen soll. Die Methode macht doch in dem Fall, wofür eigentlich der Konstruktor gedacht ist. Sollte die Methode nicht „create“ oder etwas in der Art heißen? „valueOf“ impliziert doch mehr „gib mir den Wert von“. Aber da ich die Werte als Parameter übergebe, ist der Name hier recht sinnfrei. Oder übersehe ich hier was?

    Antworten
    • Das heißt soviel wie, gib mir das ValueObject, das über diese Parameter definiert ist.

  6. Don Zampano

    16/03/2011 @ 10:12

    Man sollte versuchen, nach dem „Least astonishing principle“ zu programmieren.
    Ein VO, dass mit Vo::valueOf($bla) erzeugt werden muss, ist alles andere als least astonishing.

    Warum dann also nicht gleich
    $vo = new VO($firstname, $lastname) ?

    Ein VO soll entweder erzeugt werden können oder nicht. Dazu reicht der Konstruktor völlig.
    (Statische) validate() und getValue()-Methoden können da auch rein.

    Beispiele dazu gibt es seit Jahren in der Java- oder C#-Welt und es hat sich ein quasi-standard etabliert. Warum sollte man es bei diesen einfach zu handhabenden Konzept in PHP komplizierter machen?

    Antworten
  7. Ich kann die Argumentation schon gut verstehen. Wenn ich zwei Variablen die Zahl 3 zuweise und sie mit === vergleiche, erhalt ich true. Mache ich das gleiche mit zwei VOs (ohne statische Methode) erhalte ich false. Nicht unbedingt das, was man erwartet.

    Antworten
  8. Don Zampano

    17/03/2011 @ 09:48

    @Oskar
    Wenn ich zwei 2-Euro-Münzen mit == vergleiche, ehalte ich true.
    Wenn ich sie mit === vergleiche, was erhalte ich dann, oder besser: was sollte ich dann erhalten?

    Es geht nicht um einen technischen Vergleich von Objekten, sondern um deren Werte.
    Ergo vergleiche ich zwei VOs nun mal nicht mit === sondern mit $vo->equals($vo2).

    Antworten
    • @Don
      Wenn du 2 Euro Münzen vergleichst, dann sind das 2 Objekte, die im Bauch den gleichen Wert haben, wodurch sie aber noch lange nicht gleich sind. Ist natürlich klar. Wie wäre es denn mit dem Vergleich: 0 == false. Und so kann man VOs eben auch betrachten. Ob ich dann equals nutze oder === ist eine mE philosophische Frage. Aber da du das „least asthonishing principle“ ins Spiel gebracht hast; willst nun eine eigene Methode dem programmiersprachlichen Vorgehen bevorzugen?

  9. Don Zampano

    17/03/2011 @ 11:55

    @Oskar

    Zwei 2-Euro-Münzen sind nicht zwei Objekte, sondern zwei mal der gleiche Wert.
    Und da der Wert gleich ist, sind es die VOs auch.
    Mir wäre es egal, welche 2-Euro-Münze du mir gibst, ich will nur zwei Euro haben.

    Und weil es nur um Werte geht, ist das entscheidend, sonst würden ValueObjects nicht so heissen.

    Das ist auch keine philosophische Frage, denn wäre dein Geldbeutel voll mit 2-Euro-Münzen wären die alle aus Objektsicht nicht identisch.
    Wohl aber als Wert. Also bleibt dir nichts anderes übrig, als den Vergleich mit „===“ als nicht ausreichend bzw. falsch anzusehen. Ergo brauchst du eine andere Vergleichsmöglichkeit.

    Das wiederum ist das häufigste Problem, warum es überhaupt so viel Verwirrung gibt – weil man versucht, technische Aspekte zu betrachten, statt sich auf das Konzept eines VOs zu konzentrieren.

    Was noch garnicht angesprochen wurde, ist auch der Kontext. In dem obigen Kontext wäre es egal, welche Münze du mir gibst.
    Wären wir beide jedoch Numismatiker, wäre es whrscheinlich schon wichtig welche. D.h. ein VO in einem Kontext wäre keines in einem anderen.

    Antworten
  10. Don Zampano

    17/03/2011 @ 15:23

    Ach ja?

    Ich sagte „Mir wäre es egal, welche 2-Euro-Münze du mir gibst, ich will nur zwei Euro haben.“

    Ist „2 Euro“ auch ein Objekt?

    So schwer ist es doch nicht:
    a) Ich brauch‘ mal zwei Euro!
    b) Ich will genau diese 2-Euro-Münze und nicht eine andere

    a = VO mit Wert 2
    b = Objekt Münze

    oder anderes Beispiel:

    Du selbst bist in einem Stadion nur ein Besucher von 70.000, deine Eigenschaften sind mir egal.

    Besucher => VO

    Will ich dich einstellen, will ich schon deine Identität kennen.

    Du => Bewerber Daniel…

    So schwer ist das Konzept von VOs nun ja nicht, sonst wären sie nicht eine der am einfachsten handhabbaren und flexibelsten Konzepte. Und wie immer entscheidet der Kontext.

    Antworten
  11. Eine Münze ist ein Objekt und hat einen Wert. Das kann dann meinetwegen auch durch ein VO abgebildet werden. Letztendlich wäre ein Objekt Münze auch nur noch eine Sammlung verschiedener VOs.

    Antworten
  12. Don Zampano

    17/03/2011 @ 17:36

    Eine Sammlung verschiedener VOs? Jetzt wird’s aber abstrus.

    „2 Euro“ ist ein Wert.
    „2“ ist ein Wert.
    „Daniel Mustermann“ kann ein Wert sein.

    Nicht alles ist ein „Objekt“ im Sinne von Verhalten – also das, worum es bei Objekten eigentlich geht.

    Antworten
  13. Don Zampano

    21/03/2011 @ 12:28

    Schon klar, darum geht es aber nicht.

    Das ist die Essenz:

    Die Gleichheit zweier VOs basiert nicht auf ihrer Identität (also das gleiche Objekt zu sein, geprüft mit dem ===-Operator) sondern auf ihren Inhalt.

    Das ist analog zu einem ArrayObject. Zwei ArrayObjects sind gleich, wenn sie die gleichen Elemente beinhalten, nicht wenn sie die selben Objekte sind.

    Ergo kann der ===-Operator (identisch) bei VOs nicht zum Vergleich herangezogen werden.

    Antworten

Schreibe einen Kommentar

Pflichtfelder sind mit * markiert.