![]() |
AW: Spring-DI / DelegatedConstructor / Factory für Dummies
Zitat:
|
AW: Spring-DI / DelegatedConstructor / Factory für Dummies
Du weißt aber nicht, was du eigentlich verpasst, wenn du dich endlich mal um den Zaun kümmern würdest ;)
|
AW: Spring-DI / DelegatedConstructor / Factory für Dummies
Ich glaube, dass eine wichtige Komponente von Dir außer Acht gelassen wird. Es gibt da nämlich noch die Haftung, die Du eigentlich mit den AGBs (Eula usw) bei kommerzieller Software (und bei OSS) ausschaltest. Wenn Du aber Deiner Sorgfaltspflicht nicht nachkommst, könntest Du in eine solche aber wieder geraten( siehe
![]() Gruss Jens |
AW: Spring-DI / DelegatedConstructor / Factory für Dummies
Der (visuelle) Delphi-RAD-Ansatz, der den Einsatz mehrerer/vieler miteinander verknüpfter Komponenten bedeutet, steht einer vernünftigen, automatisierten Teststrategie entgegen.
Erzeuge ich dagegen die Komponenten aber bereits im Code, dann ist der Übergang zu DI-Containern sehr viel einfacher realisierbar. Damit ist auch die Ableitung eigener Komponenten inklusive Test viel leichter umsetzbar. Angenommen ich habe z.B. 4 Panels, die auf einen Click reagieren sollen. Delphi-RAD: Ich weise jedem Panel sein OnClick-Event zu und implementiere dort den Ablauf. Unterscheidet sich der Ablauf nicht bis auf eine Kennung, so fassen ich die 4 Aufrufe vielleicht zusammen und werte den Sender in einer IF-ELSEIF-Konstruktion aus. Werte ich dagegen sogar das TAG-Property aus, vereinfache ich es noch weiter. So macht das wohl jeder irgendwann. Was kann nun passieren? Bei einem 5. Panel "vergesse" ich das Onclick oder das Tag oder die Auswertung im OnClick. Hilfreich kann schon eine solche Konstruktion sein:
Delphi-Quellcode:
Diese Konstruktion sichert die visuellen Einstellungen im IDE-Inspektor noch einmal im Code ab. Der Aufwand ist gering, der Effekt dagegen hoch, insbesondere wenn es um zahlreiche Properties geht. Als Seiteneffekt gibt's damit auch einen Fehler, wenn einmal ein Panel versehentlich gelöscht wird.
constructor TMainForm.Create(AOwner: TComponent);
begin SetupPanelClick([pnlA, pnlB, pnlC, pnlD]); end; procedure TMainForm.SetupPanelClick(aPanels: array of TPanel); var aPanel : TPanel; begin for aPanel in aPanels do aPanel.OnClick := DoOnPanelClick; end; Eines aber bleibt: Das Panel kann mir beim Setup nicht helfen, denn woher soll es denn wissen, wofür es da ist? (Mit GUI-Tests kann ich wenigstens nocht überprüfen, ob das Panel so reagiert, wie es soll.) Erst wenn ich ein eigenes Panel ableite, das die Funktionalität beinhaltet, komme ich dem Testansatz näher: Ich verlagere die Funktionalität z.B. des "Geklickt"- Werdens in das Panel und gebe über ein Event und/oder Property das Ergebnis nach "außen". Das kann ich testen, da ist nichts komplex. (Da habe ich also meinen Zaun für die Kühe.) Ich kann nun sogar mein Panel nun hinter ein Interface IMyPanel stecken und die Verwaltung einem DI-Container überlassen. So muss ich am "Einsatzort" keine spezielle Unit für TMyPanel einbinden, sondern nur die (zentrale) Interface-Deklarations-Unit. Damit habe ich ein gut entkoppeltes System, das leicht(er) testbar ist und bin auch noch flexibel: ich kann TMyPanel schnell durch TMyBevel ersetzen (wenn das IMyPanel implementiert). Wer das bis hierhin gelesen hat, wird möglicherweise skeptisch anmerken, dass, wenn ich alles zu Fuß mache, ich ja gar keinen IDE-Designer mehr brauche. Stimmt wohl, letztlich läuft es darauf hinaus. Außerdem leben viele komplexe Komponenten-Sammlungen von automatischen Verknüpfungen, die auf RTTI basieren (leicht zu erkennen an Manager-Komponenten). Insofern: Der (visuelle) Delphi-RAD-Ansatz, der den Einsatz mehrerer/vieler miteinander verknüpfter Komponenten bedeutet, steht einer vernünftigen, automatisierten Teststrategie entgegen. |
AW: Spring-DI / DelegatedConstructor / Factory für Dummies
Zitat:
|
AW: Spring-DI / DelegatedConstructor / Factory für Dummies
Hi neo,
danke für den aufführlichen Beitrag. Das klingt an den meisten Stellen auch sehr plausibel und den Weg werde ich weiter andenken. Allerdings sehe ich das wie Stefan, dass man GUI und BL getrennt betrachten sollte. Die BL mit DI und Interfaces aufzubauen ist mit Sicherheit eine gute Sache und trägt zur Übersichtleichkeit bei der Projektwartung bei. Die GUI würde ich aber (fast) immer in der IDE zusammenklicken wollen (außer in dynamischen Bereichen, die Abhängig von BL und Daten aufgebaut werden müssen). In dem Zusammenhang ist dann natürlich eine gute Datenbindung hilfreich. Insofern ist Dein Panel-Beispiel nicht ganz passend. Das Panel sollte nur als Userinface dienen, ohne selbst irgendwelche Zustände zu verwalten. Man könnte also z.B. ein Objekt an das Panel binden, dessen Eigenschaft ClickCount erhöht wird, wenn das Panel angeklickt wird. Weiterhin zeigt das Panel in seinem Caption die Eigenschaft ClickCount des gebunden Objektes an (sofern ein Objekt angebunden ist). Dem Objekt ist es dabei wiederum wurscht, ob es irgendwelche Panels gibt, die gerade an es selbst gebunden sind. Also sehe ich nicht die Notwendigkeit, GUI-Controls über ein Framework erzeugen zu lassen. In Bezug auf die BL- und Datenschicht begeistert mich das Thema aber immer mehr... :-) Nochmal zum Testen: Stell Dir vor, in Deinem Panel-Projekt baust Du einen Timer ein, der nach 1 Stunde feuert und in dem Du versehentlich Panel3 löschst. Da kannst Du doch Unit-Tests durchführen wie Du willst und wirst das Problem nicht aufdecken. Die Panels, egal ob mit Ereignisbehandlungen oder Ableitungen werden "in sich" korrekt funktionieren. Aber im Zusammenspiel des gesamten Projektes treten dann doch Probleme auf, die nach 1 Stunde zum Tragen kommen. Ich kann nachvollziehen, dass man mit solchen Tests bestimmte versehentliche Änderungen (Seiteneffekte) in einem sehr engen Umfeld abfangen kann, aber vermutlich ist das wichtiger, wenn man im Team arbeitet. Ich denke, dass zumindest in meinen Projekten der Aufwand zum Nutzen in keinem sinnvollen Verhältnis steht. Aber auf den Rest freue ich mich schon. :-) @Stevie In Deinem letzten Satz stimmt etwas noch nicht... NO GUI? :stupid: |
AW: Spring-DI / DelegatedConstructor / Factory für Dummies
Mal noch eine andere Frage, die aber auch in den Bereich mit hineinspielt:
Im Moment erzeuge ich diverse Objekte, die ineinander verschachtelt sind. Eine Turnierveranstaltung enthält Turniere, die wieder Spiele enthalten, die wieder Spielparteien und zu spielende Sätze enthalten usw. Über eine Iteration über Spiel.Owner..Owner..Owner kann ich ermitteln, zu welchem Turnier ein bestimmtes Spiel eigentlich gehört. Ich verwalte also nicht explizit Spiel.Turnier als Eigenschaft. Über die (in einer Funktion verpackte) Iteration kann ich also für jedes Objekt bei Bedarf ermitteln, ob es in einem bestimmten Turnier enthalten ist bzw. zu einem bestimmten Turnier gehört. Wenn ich meine Objekte künftig in einer Factory erzeugen lasse und daraufhin nur noch mit Schnittstellen arbeite, fällt diese Möglichkeit ja weg - oder? Wie sollte man solche Beziehungen und Verschachtelungen am sinnvollsten verwalten? |
AW: Spring-DI / DelegatedConstructor / Factory für Dummies
Dieses Owner Verketten verletzt ganz klar das
![]() Hier auch nochmal der Hinweis: Unterscheide bei DI simple Datenklassen und Klassen, die wirklich Businesslogik übernehmen. Ein DI ist imo nicht dazu da, dir einen kompletten Objectgraph für ein Turnier, inklusive Austragungsorten, Spielen und Spielern aufzubauen (ich gehe hier davon aus, dass die besagten Objekte nur PODOs sind). |
AW: Spring-DI / DelegatedConstructor / Factory für Dummies
Ich bin in der gleichen Situation wie Stahli, arbeite also an allein an einem über die Jahre gewachsenem Projekt. Ich habe mich in letzter Zeit ebenfalls mit den hier besprochenen Teckniken beschäftigt und sie für mich verworfen. Das ganze Kram führt zu imo zu "over engeneering". Zu Spring habe ich eine Session bei der EKON besucht und diverse Literatur durchgeackert. Es ist mir gänzlich verborgen geblieben wobei mich ein solches Framework unterstützen könnte. Unit testing lohnt den Aufwand nicht, und Interfaces benutze ich nur wenn ich gezwungen bin mich mit COM zu beschäftigen. In meiner Software sehe ich keinerlei Mehrwert gegenüber abstrakten Methoden. Demeters Gesetz hat was für sich, als allgemeiner Ratschlag. Aber wer sich ständig dran hält schreibt sich die Finger Wund. Für (fast) nichts. Selbst Heiligtümer wie die GOF Patterns haben sich für mich als nur eingeschränkt nützlich erwiesen. Die eine oder andere Idee dahinter ist OK und lässt sich nutzen. Das war es aber auch schon. Wiederverwendbarkeit ist gut, aber um den alten Spruch zu bemühen: Was nach allen Seiten offen ist, ist vermutlich auch nicht ganz dicht. Offensichtlich schlage ich mich nur mit trivialen Problemen herum. Ich habe mich noch nicht in der Situation befunden, ein Problem nicht mit einer einfachen und klaren dem Anwendungsfall angepassten Objekthierachie lösen zu können. Es werden viele Säue durch viele Dörfer getrieben. Ich denke zu viele.
|
AW: Spring-DI / DelegatedConstructor / Factory für Dummies
Die "Objekte" sind von TComponent abgeleitet (damit sie einen Owner haben und ich sie ggf. auch in der IDE einrichten kann) und enthalten Daten und BL.
Will ich in einem Spiel ermitteln, ob es sich in einem Turnier befindet, nutze ich eine lokale Variable und eine externe (in einer gesonderten Unit abgelegten) Funktion.
Delphi-Quellcode:
Im Spiel dann etwa:
function OwnerTournament(C: TComponent): TodTournament;
begin Result := nil; if C is TodTournament then Result := (C as TodTournament) else if C.Owner <> nil then Result := OwnerTournament(C.Owner); end;
Delphi-Quellcode:
Auf diese Weise kennt ein Spiel zwar nicht unmittelbar sein Turnier, kann dieses aber bei Bedarf ermitteln und dort z.B. die Ermittlung der Platzierungen veranlassen (das sollte ja erfolgen, wenn ein Spiel neue Ergebnisse hat).
procedure TodGame.Calc;
var MyTournament: TodTournament; begin ... MyTournament := OwnerTournament(Self); if Assigned(MyTournament) then MyTournament.Calc; ... end; Für SOOO schlecht halte ich dieses Konstrukt eigentlich nicht. Deinen letzten Satz kann ich nicht im Detail nachvollziehen. Sollte man BL-Klassen und Datenklassen getrennt definieren und erzeugen? Wo liegt der Vorteil? Im Moment kann der User ein neues Projekt anlegen, was einem TodTournamentEvent.Create() entspricht. Einige SubObjekte werden dann automatisch (leer) hinzugefügt, z.B. Sportart und Ort. Diese SubObjekte können dann durch den User über bestimmte Formulare bearbeitet und gefüllt werden. Andere Objekte erzeugt der User bei Bedarf komplett neu (z.B. Vereine und deren Mitglieder). Jedes Objekt beinhaltet seine eigene BL und kann sich bei Bedarf in seinem Umfeld zurecht finden. Starre Verdrahtungen gibt es aber kaum. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 19:56 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz