Delphi-PRAXiS
Seite 2 von 10     12 34     Letzte »    

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Spring-DI / DelegatedConstructor / Factory für Dummies (https://www.delphipraxis.net/166192-spring-di-delegatedconstructor-factory-fuer-dummies.html)

stahli 9. Feb 2012 11:25

AW: Spring-DI / DelegatedConstructor / Factory für Dummies
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1150071)
Dann geht es dir wie dem Bauern, der keine Zeit hat, einen Zaun zu bauen, weil er die Kühe einfangen muss...

[OT]Ach das geht schon erst mal. Um die Weide ist ein kleiner Graben und wenn die Melkanlage erst mal auf vollen Touren läuft, dann schaue ich mir an, wie man am besten stabile Zäune baut :-) [/OT]

s.h.a.r.k 9. Feb 2012 12:04

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 ;)

Jens01 9. Feb 2012 13:20

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 BGB). Und ein Unittesting könnte bei kommerzieller Software ein Richter schon als Sorgfalt auslegen(, meine ich, der zwar kein Rechtsanwalt ist, aber mit den Jungs viel zu tun hatte).
Gruss Jens

neo4a 10. Feb 2012 08:56

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:
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;
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.

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.

Stevie 10. Feb 2012 09:32

AW: Spring-DI / DelegatedConstructor / Factory für Dummies
 
Zitat:

Zitat von neo4a (Beitrag 1150264)
Der (visuelle) Delphi-RAD-Ansatz, der den Einsatz mehrerer/vieler miteinander verknüpfter Komponenten bedeutet, steht einer vernünftigen, automatisierten Teststrategie entgegen.

Da muss ich dir ausnahmsweise mal widersprechen. Es kommt darauf an, wie weit ich den RAD-Ansatz gehe. Ich sehe es bei uns in der Software, dass ich manchmal einen Frame aufmache und mich eine graue gähnende Leere anschaut und ich erst im Source rumwühlen muss, um zu sehen, wo welche Komponenten erzeugt und platziert werden. Das ist für mich persönlich der Horror. Wo man dann die Businesslogik implementiert, steht auf einem anderen Blatt. Und dort kann dann sehr wohl DI zum Zuge kommen. Dennoch würde ich niemals meine GUI im Source zusammen bauen.

stahli 10. Feb 2012 10:04

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:

stahli 10. Feb 2012 11:47

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?

Stevie 10. Feb 2012 12:35

AW: Spring-DI / DelegatedConstructor / Factory für Dummies
 
Dieses Owner Verketten verletzt ganz klar das Gesetz von Demeter. Dein Beispiel zeigt für mich ein gravierendes Designproblem. Sofern der angesprochene Code innerhalb einer TSpiel Klasse ist und diese implizit eine Abhängigkeit auf ein TTurnier hat, dann solltest du das auch dementsprechendüber eine TTurnier Eigenschaft in deiner TSpiel Klasse implementieren und nicht auf Implementierungsdetails an anderen Stellen (zum Beispiel über das Verketten von Owner) vertrauen. Wenn sich diese verändern, bricht dein Code an einer ganz anderen Stelle auseinander.

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).

exilant 10. Feb 2012 13:18

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.

stahli 10. Feb 2012 13:34

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:
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;
Im Spiel dann etwa:

Delphi-Quellcode:
procedure TodGame.Calc;
var
  MyTournament: TodTournament;
begin
  ...
  MyTournament := OwnerTournament(Self);
  if Assigned(MyTournament) then
    MyTournament.Calc;
  ...
end;
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).

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 14:56 Uhr.
Seite 2 von 10     12 34     Letzte »    

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz