AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Sprachen und Entwicklungsumgebungen Object-Pascal / Delphi-Language Delphi Class Helper als Lösung für zirkuläre Unit-Referenzen
Thema durchsuchen
Ansicht
Themen-Optionen

Class Helper als Lösung für zirkuläre Unit-Referenzen

Offene Frage von "jaenicke"
Ein Thema von Der schöne Günther · begonnen am 25. Aug 2013 · letzter Beitrag vom 27. Aug 2013
Antwort Antwort
Seite 1 von 2  1 2      
Der schöne Günther

Registriert seit: 6. Mär 2013
6.115 Beiträge
 
Delphi 10 Seattle Enterprise
 
#1

Class Helper als Lösung für zirkuläre Unit-Referenzen

  Alt 25. Aug 2013, 19:35
Delphi-Version: XE2
Diesiges Wetter. Zeit, wieder über zirkuläre Unit-Referenzen zu plauschen

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: Solving circular unit references with class helpers. Ich habe hier im Forum noch keine heißblütig geführten Diskussionen dazu gefunden. Deshalb nun dieses Thema.

Zusammenfassung: Altbewährtesbekanntes Halten einer (protected) Referenz vom Typ TObject , Casten auf den richtigen Typ mittels Helferklassen-Methode (oder Property).

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;

Geändert von Der schöne Günther (25. Aug 2013 um 19:39 Uhr) Grund: Kurzbeispiel hinzugefügt
  Mit Zitat antworten Zitat
silver-moon-2000

Registriert seit: 18. Feb 2007
Ort: Schweinfurt
170 Beiträge
 
Delphi XE Professional
 
#2

AW: Class Helper als Lösung für zirkuläre Unit-Referenzen

  Alt 26. Aug 2013, 00:10
Hier nun eine interessante Methode, zirkuläre Referenzen mit Class Helpern noch einmal neu anzugehen
Wenn ich das Thema ClassHelper richtig verstanden habe, habe ich doch durch die ClassHelper eine THund-"PseudoKlasse" in der folgenden Form
Delphi-Quellcode:
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;
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?

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

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
Tobias
Bitte nicht hauen , ich weiß es nicht besser
  Mit Zitat antworten Zitat
Benutzerbild von bernau
bernau

Registriert seit: 1. Dez 2004
Ort: Köln
1.268 Beiträge
 
Delphi 11 Alexandria
 
#3

AW: Class Helper als Lösung für zirkuläre Unit-Referenzen

  Alt 26. Aug 2013, 06:00
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.
Denk bei ClassHelpern immer daran: Es kann nur einen geben.

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).
Gerd
Kölner Delphi Usergroup: http://wiki.delphitreff.de
  Mit Zitat antworten Zitat
Furtbichler
(Gast)

n/a Beiträge
 
#4

AW: Class Helper als Lösung für zirkuläre Unit-Referenzen

  Alt 26. Aug 2013, 06:27
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:
Type
  THundebesitzer = class;
  THund = class
     fHundebesitzer : THundebesitzer;
  public
     Hundebesitzer : THundebesitzer read ....;
  End;
Wieso verwendest Du nicht einfach Interfaces? Wenn man das konsequent durchzieht und keine kranken Sperenzien anstellt, gibt es auch keine Probleme.

Geändert von Furtbichler (26. Aug 2013 um 06:31 Uhr)
  Mit Zitat antworten Zitat
TiGü

Registriert seit: 6. Apr 2011
Ort: Berlin
3.062 Beiträge
 
Delphi 10.4 Sydney
 
#5

AW: Class Helper als Lösung für zirkuläre Unit-Referenzen

  Alt 26. Aug 2013, 07:14
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).
  Mit Zitat antworten Zitat
Furtbichler
(Gast)

n/a Beiträge
 
#6

AW: Class Helper als Lösung für zirkuläre Unit-Referenzen

  Alt 26. Aug 2013, 07:17
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).
Hier geht es nicht um Design, sondern um die Lösung eines allgegenwärtigen Problems: Zirkuläre Referenzen. In diesem Fall *soll* es ja so sein, das ein Hund maximal einen Besitzer hat. Das ist sozusagen Constraint-By-Design. Bei deiner Lösung müsste man das in der Dictionary-Klasse explizit ausprogrammieren.

Tendentiell ist so eine Relationsklasse natürlich ein schöner Ansatz, wenn die Besitzverhältnisse mit Logik verbunden sind.
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.401 Beiträge
 
Delphi 11 Alexandria
 
#7

AW: Class Helper als Lösung für zirkuläre Unit-Referenzen

  Alt 26. Aug 2013, 07:46
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:
// Zum Beispiel nicht:
Hund.Bellen;
Hund.Besitzer.Schimpfen;

// sondern:
Hund.OnBellt := Besitzer.HundBelltEvent;

procedure TBesitzer.HundBelltEvent;
begin
  Schimpfen;
end;

// und dann nur:
Hund.Bellen;
// EDIT:
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.
Sebastian Jänicke
Alle eigenen Projekte sind eingestellt, ebenso meine Homepage, Downloadlinks usw. im Forum bleiben aktiv!

Geändert von jaenicke (26. Aug 2013 um 07:49 Uhr)
  Mit Zitat antworten Zitat
Der schöne Günther

Registriert seit: 6. Mär 2013
6.115 Beiträge
 
Delphi 10 Seattle Enterprise
 
#8

AW: Class Helper als Lösung für zirkuläre Unit-Referenzen

  Alt 26. Aug 2013, 09:32
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.
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?
Das habe ich in dem Kurzbeispiel ausgelassen: Den gleichen Class Helper hätte ich jetzt in den implementation -Teil von Hund.pas gesetzt. Somit muss ich auch intern in THund den Zeiger in den Nebel nicht casten. Außerdem könnte ich diesen "internen" Helper ja noch um eine zweite Property (bzw. Getter-Methode) erweitern, die ein anderes Verhalten aufweist als die "normale" für "von außen". Auch wenn das jetzt nicht mein Fall wäre...


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.
Guter Einwand! Das wird bestimmt zu oft übersehen (auch von mir).

Wieso verwendest Du nicht einfach Interfaces? Wenn man das konsequent durchzieht und keine kranken Sperenzien anstellt, gibt es auch keine Probleme.
Vielleicht ist es für mich noch zu früh am Morgen - Aber wo hilft mir das? Angenommen beide Klassen müssen sich gegenseitig kennen. Sie sind so unterschiedlich, ich finde keine sinnvolle Abstraktion die auf beide zutrifft. IHund muss nun eine Property vom Typ IHundesitzer nach außen führen und umgekehrt. Oder war das anders gedacht? Interface nur für einen von beiden (wie auch hier nur Helper-Klasse für einen)?

Außerdem hätte ich spontan Angst vor der automatischen Referenzzählung durch Interfaces - Mit der habe ich noch keine Erfahrung: Zwei Instanzen referenzieren sich nun gegenseitig, außerhalb davon hat die Welt allerdings bereits mit ihnen abgeschlossen. Nun trudeln die beiden auf ewig Hand in Hand durch meinen Speicher. Kennt Delphi etwas wie "Weak References"?

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.
Wenn ich kleine Kinder in Angst und Schrecken versetzen will, tue ich das mit der System.pas: Knappe 40.000 Zeilen kann doch keiner mehr ernst meinen. Da braucht man sich dann auch nicht mehr zu wundern, wenn man für ein "Hallo Welt" schon 20 Megabyte braucht.

Geändert von Der schöne Günther (26. Aug 2013 um 09:35 Uhr)
  Mit Zitat antworten Zitat
Daniel
(Co-Admin)

Registriert seit: 30. Mai 2002
Ort: Hamburg
13.919 Beiträge
 
Delphi 10.4 Sydney
 
#9

AW: Class Helper als Lösung für zirkuläre Unit-Referenzen

  Alt 26. Aug 2013, 09:40
Da braucht man sich dann auch nicht mehr zu wundern, wenn man für ein "Hallo Welt" schon 20 Megabyte braucht.
Doch, eigentlich schon - das mit den Klassen hat der Linker ganz gut drauf - wenn Du 10 Klassen in einer Unit hast und nur eine verwendest, landet auch nur die in der EXE. Das allein ist kein Argument gegen mehrere Klassen in einer Unit.
Daniel R. Wolf
mit Grüßen aus Hamburg
  Mit Zitat antworten Zitat
Der schöne Günther

Registriert seit: 6. Mär 2013
6.115 Beiträge
 
Delphi 10 Seattle Enterprise
 
#10

AW: Class Helper als Lösung für zirkuläre Unit-Referenzen

  Alt 26. Aug 2013, 09:47
Stimmt! Da hätte ich auch selber drauf kommen können, da der Linker mir immer gerne Klassen rausoptimiert hatte, die im Quelltext nicht direkt erwähnt, sondern nur indirekt über die RTTI gesucht wurden

Nur in den alten Pascal-Units (wie System.pas) ist ja an sich mindestens 90% eben nicht objektorientiert, sondern nur alleine rumbaumelnde Funktionen. Ob der Linker das auch kann?
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      

 

Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 10:01 Uhr.
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