Einzelnen Beitrag anzeigen

r2c2

Registriert seit: 9. Mai 2005
Ort: Nordbaden
925 Beiträge
 
#23

AW: Komponente ableiten

  Alt 7. Sep 2010, 22:13
Eine Geburt oder eine Hochzeit sind genauso Ereignisse wie eine Taufe oder eine Scheidung.
Das ist klar und unproblematisch. Nur heißt das nicht, dass man das auch modellieren muss. Jede Person ist ein Lebewesen. Außerdem gibt es unterschiedliche Personen: Männer und Frauen. Achso und Kinder gibt es auch noch. Also Mädchen und Jungen. ... Trotzdem wirst du in deinem Programm keine TLivingBeing, TMan, TWoman, TGorl und TBoy finden. UNd TNose, TLeftEye und TRightEye auch nicht. OK, das sind jetzt Extrembeispiele. Was ich damit sagen will: Nur die Tatsache, dass es logisch gesehen diese Klassifizierung gibt, impliziert noch lange nicht, dass das auch so modelliert werden muss. Jede zusätzliche Klasse bringt zusätzliche Komplexität.

Zitat:
Nicht immer gibt es eindeutige Belege über den Zeitpunkt eines Ereignisses, deshalb können auch Zeitspannen (z.B. zwischen dem 31.01.2009 und 03.04.2009) angegeben werden.
Das ist auch unstrittig. Deshalb gibt es TVagueDate.

Zitat:
Alle diese exemplarischen Ereignistypen haben dieselben Standardeigenschaften (z.B. Datum, Ort). Deshalb habe ich neue Klassen von TEvent abgeleitet und beispielsweise bei TMarriage noch den Taufpaten als Eigenschaft hinzugefügt, verstehst du?

Ein Umzug, also ein Wohnortwechsel ist genauso ein Ereignis wie z.B. eine Volkszählung. Das war mein Hintergrundgedanke bei der ganzen Sache.
Dein Gedanke ist mir klar. Er resultiert meiner Meinung nach aus der typischen Überbewertung der Vererbung.

Wenn man anfängt, OO zu lernen, bleibt einem wohl aus irgendwelchen Gründen die Vererbung besonders gut im Gedächtnis. Der Vorteil erscheint augenblicklich klar: Gleiche Eigenschaften und Methoden werden in Basisklassen definiert und an die Kindklassen vererbt. So muss man das gleiche Zeug nicht mehrmals schreiben. Der Punkt ist: Eigentlich ist Vererbung, genauer: Generalisierung/Spezialisierung viel mehr. Ich hab jetzt nicht die Zeit, das vollständig zu erläutern, aber um mal einen Eindruck zu geben: Der eigentlich Punkt ist, dass man mit den Kindklassen genauso umgehen kann wie mit der Basisklasse. Das werden wir uns wohl später bei der Datenspeicherung zunutze machen [1]: Wir leiten alle zu speichernden Klassen von ner gemeinsamen Basisklasse ab und schreiben Code, der uns erlaubt ein beliebiges Objekt dieser Klasse zu speichern. Wir machen uns also ne Liste mit allen Elementen, die gespeichert werden sollen. Unser Code zum Speichern wird nun die liste in ner Schleife durchgehen und je eine Methode (save) zum Speichern aufrufen.
Delphi-Quellcode:
// Pseudocode
for elem in AllElements do
begin
  elem.save;
end;
Diese vier Zeilen werden es wohl letztendlich im Grunde genommen sein. elem kann eine TPerson sein. Oder ein TPartnership oder ein TAdoption. Egal. Der Code behandelt alle gleich. Es wird auch keine explizite Unterscheidung gemacht "if elem is TPerson then". Das alles passiert - wenn man richtig macht - automatisch durch Vererbung und Polymorphie, d.h. durch dynamische Bindung.

Jetzt meine Frage: Wo wirst du im Code eine Variable als TEvent und nicht als TBirth deklarieren? Wo? Nirgends, denn überall ist der Unterschied zwischen TBirth und TMarriage signifikant. Das sollte schonmal ein Alarmsignal sein. Wenn du einen Typen definierst, den du nie benutzt, ist zwar nicht zwangsläufig, aber doch vermutlich was faul.

Zweiter Punkt: Ist TMarriage nun ein TEvent oder ein TPartnership? Aus gutem Grund kann es nicht beides sein.

Dritter Punkt: Leere Klassen. Du wirst dadurch Klassen haben, die ganz oder fast leer sind. Dem Tod kannst du maximal noch ne Ursache zuweisen, aber essenziell wird das Ding leer sein. TDeath tut nichts anderes als TEvent, es verhält sich mangels Methoden sogar überhaupt nicht. Auch das ist wieder so etwas, wo die Alarmglocken läuten sollten. Das *kann* OK sein. Ist aber oft ein Zeichen für falsches Vorgehen. Oben hab ich irgendwo "OOD and Coffee" verlinkt. Lies das mal. Robert C. Martin beschreibt da wirklich gut, was falsche Abstraktionen sind.

Was ist also die Alternative?
- TPerson hat ein DateOfBirth: TVagueDate
- Und ein DateOfDeath: TVagueDate.
- Wenn du unbedingt noch die Todesursache haben willst, hat TPerson eben auch noch ein CauseOfDeath: string.
- Ansonsten kann die Todesursache, sofern sie mal ausnahmsweise von Bedeutung ist, ja auch als Notiz hinzugefügt werden. Wenn du in ner späteren Version n Feature "Die häufigsten Todesursachen" einbauen willst, kannst du immer noch das CauseOfDeath-Feld einführen. Bis dahin reicht IMHO die Notiz.
==> So wird ein Programm bedeutend einfacher. Wenn du das dann mal implementierst, wirst du merken, dass es auch so schon kompliziert genug ist. Allein schon das freigeben wird dich vermutlich ein paar Stunden kosten, bis dus einigermaßen fehlerfrei implementiert hast. Je nachdem, wie viel ich dir dabei helfe. Denke also nicht, dass du nicht auf Einfachheit achten müsstest. Das, was du vor hast, ist kein kleines Programm mehr.

[1] Achtung! Das ist noch nicht durchdacht. Bisher nur meine vage Vorstellung von dem, was wir letztendlich tun werden. Das muss ich mir später mal genauer durch den Kopf gehen lassen.

mfg

Christian
Kaum macht man's richtig, schon klappts!
  Mit Zitat antworten Zitat