![]() |
Delphi-Version: XE2
Class Helper als Lösung für zirkuläre Unit-Referenzen
Diesiges Wetter. Zeit, wieder über zirkuläre Unit-Referenzen zu plauschen :roteyes:
Vielleicht bin ich noch zu neu in Delphi, aber ich finde das Thema ist nicht gegessen. Ich habe aktuell auch (glücklicherweise nur ein oder zwei) Fälle, in denen sich zwei Klassen gegenseitig kennen müssen. Alles direkt mit "Das ist schon Zeuge genug für schlechtes Design, mach es irgendwie anders" abzuwürgen ist mir zu billig. Hier nun eine interessante Methode, zirkuläre Referenzen mit Class Helpern noch einmal neu anzugehen: ![]() Zusammenfassung: Altbewährtesbekanntes Halten einer (protected) Referenz vom Typ
Delphi-Quellcode:
, Casten auf den richtigen Typ mittels Helferklassen-Methode (oder Property).
TObject
Was haltet ihr davon? Das einzige was mich stört: Man könnte vergessen, die Helfer-Unit einzubinden und sich anschließlich wundern, wo denn der Verweis (bsp. des Hundes auf seinen Halter) steckt. Hier einmal noch die Kurzfassung:
Delphi-Quellcode:
// Hundebesitzer.pas
uses Hund ; type THundebesitzer = class public var name: String; hund: THund; end;
Delphi-Quellcode:
// Hund.pas
type THund = class protected var // An 'private' kommt auch ein Helper nicht mehr ran besitzer_uncasted: TObject; public var name: String; end;
Delphi-Quellcode:
// HundHelper.pas
uses Hund, Hundbesitzer ; type THundHelper = class helper for THund private function GetBesitzer: THundebesitzer; procedure SetBesitzer(const Value: THundebesitzer); public property besitzer: THundebesitzer read Getbesitzer write Setbesitzer; end; implementation function THundHelper.Getbesitzer: THundebesitzer; begin if besitzer_uncasted is THundebesitzer then Result := besitzer_uncasted as THundebesitzer else Result := nil //Oder Exception? ; end; |
AW: Class Helper als Lösung für zirkuläre Unit-Referenzen
Zitat:
Delphi-Quellcode:
Ich kann also innerhalb von THund auf den Besitzer entweder über das property direkt zugreifen oder über das ungecastete Objekt, wobei ich dann jedes Mal casten muss?
type
THund = class private function GetBesitzer: THundebesitzer; //aus ClassHelper procedure SetBesitzer(const Value: THundebesitzer);//aus ClassHelper protected besitzer_uncasted: TObject; public name: String; property besitzer: THundebesitzer read Getbesitzer write Setbesitzer;//aus ClassHelper end; Was mich dabei stören würde, wäre die Tatsache, dass ich im Setter / Getter des properties keine zusätzliche Logik unterbringen darf (von dem Cast einmal abgesehen), wenn ich nicht will, dass diese zusätzliche Logik bei jeder Verwendung des properties ausgelöst wird. In dem Beispiel vielleicht jetzt schwerer einsichtig, aber manchmal muss/möchte man zwischen einem Zugriff von innerhalb und außerhalb der Klasse unterscheiden (z.B. wenn man zusätzlich Aktionen durchführen möchte, wenn "von außen" (wozu properties imho gedacht sind) auf das Objekt zugegriffen wird).
Delphi-Quellcode:
Wenn ich nun will, dass die Namensabfrage des neuen Besitzers nur stattfindet, wenn ich den Benutzer von außen setze und nicht von innerhalb der Klasse THund, dann würde der Zugriff von innerhalb nur über das ungecastete Object (das ich dann casten muss) und der Zugriff von außen über das property stattfinden müssen.
procedure THundeHelper.SetBesitzer(const Value: THundebesitzer)
begin if Assigned(value) then begin besitzer_uncasted := value; if value.name = "Der schöne Günther" then //schlabbereNeuenBesitzerAb else //knurreNeuenBesitzerAn end; end; Oder bringt man in Settern/Gettern sowieso keine zusätzliche Logik unter? Ist das schlechter Programmierstil? Ich hoffe, ich konnte meine Gedanken halbwegs klar formulieren |
AW: Class Helper als Lösung für zirkuläre Unit-Referenzen
Zitat:
Wenn du jetzt schon einen Classhelper für deine Klassen vergibst, kannst du keinen weiteren ClassHelper mehr für die Klasse verwenden. Classhelper sind ja eigendlich dazu gedacht, an eine Klasse etwas anzuflanschen, weil man an die Ursprungs-Unit nicht dran kommt. Du verbaust quasi anderen die Möglichkeit deine Klasse zu erweitern (ohne eine Ableitung zu schreiben). |
AW: Class Helper als Lösung für zirkuläre Unit-Referenzen
Diese Lösung mit dem Zeiger-In-Den-Nebel (TObject) ist doch -ehrlich gesagt- Quark. Innerhalb von THund kannst Du ja gar nicht auf den Hundebesitzer zugreifen. Somit ist diese Umschiffung (eine Lösung ist das ja nicht) des Henne-Ei-Problems mittels Classhelper vielleicht ein Notbehelf, aber imho ein ziemlich einengender.
Das Problem tritt ja dann auf, wenn man eine eigentlich saubere Implementierung in unterschiedliche Units aufteilt. Wären sie in einer Unit, gäbe es das Problem nicht:
Delphi-Quellcode:
Wieso verwendest Du nicht einfach Interfaces? Wenn man das konsequent durchzieht und keine kranken Sperenzien anstellt, gibt es auch keine Probleme.
Type
THundebesitzer = class; THund = class fHundebesitzer : THundebesitzer; public Hundebesitzer : THundebesitzer read ....; End; |
AW: Class Helper als Lösung für zirkuläre Unit-Referenzen
Und was ist mit der Möglichkeit, in einer dritten Unit ein generisches Dictionary anzulegen, was die Zuordnung von Hund und Hundehalter speichert?
Außerdem könnte so ein Hundebesitzer leicht mehrere Hunde haben oder umgekehrt ein Hund zwei Besitzer (Herr und Frau Müller). |
AW: Class Helper als Lösung für zirkuläre Unit-Referenzen
Zitat:
Tendentiell ist so eine Relationsklasse natürlich ein schöner Ansatz, wenn die Besitzverhältnisse mit Logik verbunden sind. |
AW: Class Helper als Lösung für zirkuläre Unit-Referenzen
In den meisten Fällen lässt sich das auch viel einfacher durch eine saubere Schnittstelle lösen. Denn die Frage ist doch was man von dem Objekt Hund im Objekt Besitzer machen will. Meistens kann man das auch über Events oder ähnliches lösen.
Delphi-Quellcode:
// EDIT:
// Zum Beispiel nicht:
Hund.Bellen; Hund.Besitzer.Schimpfen; // sondern: Hund.OnBellt := Besitzer.HundBelltEvent; procedure TBesitzer.HundBelltEvent; begin Schimpfen; end; // und dann nur: Hund.Bellen; Interfaces lösen das Problem auch nicht, wenn man tatsächlich eine echte Referenz in beide Richtungen will. Man könnte allerdings die Referenz in einem abgeleiteten Interface unterbringen und nur das Basisinterface zurück referenzieren. Das geht bei Klassen aber auch. |
AW: Class Helper als Lösung für zirkuläre Unit-Referenzen
Zitat:
|
AW: Class Helper als Lösung für zirkuläre Unit-Referenzen
Ich packe Klassen, die sich gegenseitig benötigen, einfach in dieselbe Unit. So schlimm ist das gar nicht. Lange Units sind halt auch irgendwie der Pascal-Way... guckt euch doch nur mal die VCL an. Und zur Not könnte man immer noch Include-Dateien verwenden – jeweils Implementation und Interface getrennt. Damit bastelt man sich sowas ähnliches wie bei C++.
|
AW: Class Helper als Lösung für zirkuläre Unit-Referenzen
Zitat:
Grüße |
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:11 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 by Thomas Breitkreuz