Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi Class Helper als Lösung für zirkuläre Unit-Referenzen (https://www.delphipraxis.net/176296-class-helper-als-loesung-fuer-zirkulaere-unit-referenzen.html)

Der schöne Günther 25. Aug 2013 19:35

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: 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
Delphi-Quellcode:
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;

silver-moon-2000 26. Aug 2013 00:10

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

Zitat von Der schöne Günther (Beitrag 1226150)
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

bernau 26. Aug 2013 06:00

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

Zitat von Der schöne Günther (Beitrag 1226150)
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).

Furtbichler 26. Aug 2013 06:27

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

TiGü 26. Aug 2013 07:14

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

Furtbichler 26. Aug 2013 07:17

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

Zitat von TiGü (Beitrag 1226181)
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.

jaenicke 26. Aug 2013 07:46

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

Stevie 26. Aug 2013 07:48

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

Zitat von Der schöne Günther (Beitrag 1226150)
An 'private' kommt auch ein Helper nicht mehr ran

Doch, kommt er - man muss nur Self davor schreiben.

Namenloser 26. Aug 2013 08:17

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

Lemmy 26. Aug 2013 08:24

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

Zitat von jaenicke (Beitrag 1226184)
// 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.

natürlich lösen die Interfaces das Problem. Zumindest wenn man Interfaces wie Interfaces verwendet und nicht wie KLassen :-)

Grüße

bernau 26. Aug 2013 08:30

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

Zitat von namenlozer (Beitrag 1226189)
ich packe klassen, die sich gegenseitig benötigen, einfach in dieselbe unit. So schlimm ist das gar nicht.

dito.

Der schöne Günther 26. Aug 2013 09:32

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

Zitat von Furtbichler (Beitrag 1226179)
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.

Zitat:

Zitat von silver-moon-2000 (Beitrag 1226175)
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
Delphi-Quellcode:
implementation
-Teil von
Delphi-Quellcode:
Hund.pas
gesetzt. Somit muss ich auch intern in
Delphi-Quellcode:
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...


Zitat:

Zitat von jaenicke (Beitrag 1226184)
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).

Zitat:

Zitat von Furtbichler (Beitrag 1226179)
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.
Delphi-Quellcode:
IHund
muss nun eine Property vom Typ
Delphi-Quellcode:
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"?

Zitat:

Zitat von NamenLozer (Beitrag 1226189)
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.

Daniel 26. Aug 2013 09:40

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

Zitat von Der schöne Günther (Beitrag 1226201)
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.

Der schöne Günther 26. Aug 2013 09:47

AW: Class Helper als Lösung für zirkuläre Unit-Referenzen
 
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 :-D

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?

jaenicke 26. Aug 2013 10:21

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

Zitat von Lemmy (Beitrag 1226190)
natürlich lösen die Interfaces das Problem. Zumindest wenn man Interfaces wie Interfaces verwendet und nicht wie KLassen :-)

Sofern du zwei Interfaces hast und die Referenz jeweils auf das andere drin hast, geht das auch nicht. Wenn du ein Basisinterface auf einer der beiden Seiten referenzierst, geht das, aber das geht auch bei Klassen.

Und wenn du ein zusätzliches Interface benutzt, musst du erst dieses zusätzliche Interface nehmen, ggf. mit as drauf casten z.B., das ist auch nicht so schön. Aber es kommt halt drauf an was man genau braucht.

Wenn man die benötigten Funktionen der Besitzerklasse in eine Klasse TCustomBesitzer packt und die Klasse Hund erst in TBesitzer aus einer anderen Unit referenziert, funktioniert es jedenfalls auch.

Die beste Lösung ist aber Kreuzreferenzen zu vermeiden und nicht diese durch Interfaces oder ähnliches zu forcieren, wobei man bei Interfaces dann auch noch aufpassen muss, dass man keine Kreise einbaut, so dass die Objekte nie freigegeben werden.
Dass das Vermeiden nicht immer geht, sieht man ja an den weak references im ARM Compiler für iOS, die genau so etwas dort erlauben.

Furtbichler 26. Aug 2013 12:51

AW: Class Helper als Lösung für zirkuläre Unit-Referenzen
 
Interfaces kann man in eine Unit schmeißen, Klassen sollte man -der besseren Übersicht halber- aufteilen. Wenn man das so macht, lösen Interfaces diese Probleme der zirkulären Referenz.

Klar, alles in eine Datei schmeißen geht auch, aber ich hatte da mal dann ca. 10.000 Zeilen in einer Datei und das ist irgendwie uncool.

Dann lieber 500 Zeilen Interfacegedöns in einer Datei und 50 Klassen in 50 separaten Dateien.

Wer das alles partout ausschließen will, bricht sich eben einen ab (z.B. mit anonymen Verweisen, irgendwelchen Drittklassen, die die Beziehung verwalten usw. usw.)

Ich denk dabei immer an KISS. Und zwar so S wie möglich, also KIASAPDABWGU ("keep it as simple as possible, dumb ass, but without getting unübersichtlich"). Oder so ähnlich jedenfalls.

Und wichtig ist: Die Prämisse (KISS+Zusätze) ist am Wichtigsten. Wer meint, eine Datei reicht und ist KISS. Super. Wenn die Kollegen einverstanden sind => Cool. Genauso verhält es sich mit allen angebotenen Lösungen.

Bjoerk 26. Aug 2013 13:59

AW: Class Helper als Lösung für zirkuläre Unit-Referenzen
 
Die ClassHelper Lösung finde ich ziemlich suboptimal. Man schau sich nur einmal diese Zeile an:
Delphi-Quellcode:
Writeln(Dog.Master.Dog.Master.Dog.Master.Dog.Name); // "Bobby"
Da fängt ja der Hund an zu jaulen, wenn er das lesen könnte. :-D

Der schöne Günther 26. Aug 2013 14:04

AW: Class Helper als Lösung für zirkuläre Unit-Referenzen
 
Ja aber genau das ist doch die Intention. Zwei nicht verwandte Instanzen halten beide gegenseitig eine Referenz auf eine artfremde Instanz.

Ob man das jetzt schön findet, was man syntaktisch damit machen kann wenn der Verweis public ist ist doch eine andere Geschichte.

Der schöne Günther 26. Aug 2013 14:19

AW: Class Helper als Lösung für zirkuläre Unit-Referenzen
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich habe aus Versehen einen neuen Beitrag verfasst und kann ihn nicht löschen. Sorry :oops:

Zitat:

Zitat von Furtbichler (Beitrag 1226237)
Interfaces kann man in eine Unit schmeißen, Klassen sollte man -der besseren Übersicht halber- aufteilen.

Ich komme wirklich noch nicht hinterher :-(
Interfaces muss ich in die gleiche Unit packen. Und wenn ich nun schon zwei artfremde Interfaces in eine Datei packe, kann ich auch gleich die paar Zeilen mehr für die Klassen angehen und wieder alles in einen Topf werfen.

Alternativ (ähnlich den Helpern) für eine der beiden ein Interface. Wenn man sich die Abbildung im Anhang anschaut: So?

Bjoerk 26. Aug 2013 15:08

AW: Class Helper als Lösung für zirkuläre Unit-Referenzen
 
Ich verstehe deinen Wunsch, eine allgemeine Lösung zu finden. :thumb:

Solche generelle Lösungen haben aber auch einen Nachteil, weil sie eigentlich nirgends richtig passen (Kontext).

Hier halt einfach: Das Herrchen hat einen Hund. Damit weiß das Herrchen immer Bescheid. Wenn der Hund was übers Herrchen wissen muß, dann halt per Events. :wink:

Furtbichler 26. Aug 2013 16:15

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

Zitat von Der schöne Günther (Beitrag 1226249)
Interfaces muss ich in die gleiche Unit packen.

Nein, natürlich nicht. Aber hier schon (wg. zirkulärer Referenz).
[/QUOTE] Und wenn ich nun schon zwei artfremde Interfaces in eine Datei packe, kann ich auch gleich die paar Zeilen mehr für die Klassen angehen und wieder alles in einen Topf werfen.[/QUOTE]
Ich sagte doch: Eine Datei mit allen Interfaces ist allemal übersichtlicher als eine Datei mit allen Klassen.
Delphi-Quellcode:
Type
  IBesitzer = interface ...
    IHund : Hund;
  end;


  IHund = interface
   Besitzer : IBesitzer;
  end;
Und die Implementierung in separaten Dateien.

Lemmy 26. Aug 2013 20:09

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

Zitat von Furtbichler (Beitrag 1226270)

Ich sagte doch: Eine Datei mit allen Interfaces ist allemal übersichtlicher als eine Datei mit allen Klassen.
...
Und die Implementierung in separaten Dateien.


genau. Und das Vorgehen bewahrt dann einen auch davor an Stellen "aus Versehen" die Implementierung (=Klasse) zu verwenden, wo das Interface ausreicht.

jaenicke 26. Aug 2013 20:16

AW: Class Helper als Lösung für zirkuläre Unit-Referenzen
 
Eine andere Lösung ist eben die von mir angesprochene. Die bevorzuge ich eher. Wir benutzen normalerweise auch eine Datei pro Interface und eine pro Klasse. Mit der Lösung funktioniert das problemlos, egal ob mit Interface oder Klasse:
Delphi-Quellcode:
  TCustomDogOwner = class
  public
    procedure Schimpfen;
  end;

  TDog = class
  private
    FOwner: TCustomDogOwner;
  public
    property Owner: TCustomDogOwner read FOwner write FOwner;
  end;

  TDogOwner = class
  private
    FDog: TDog;
  public
    property Dog: TDog read FDog write FDog;
  end;

Furtbichler 26. Aug 2013 21:22

AW: Class Helper als Lösung für zirkuläre Unit-Referenzen
 
Die habe ich auch mal verwendet, aber das ist eben ein Trick, der den Code verunstaltet. TDogOwner führt keine neuen Methoden oder Eigenschaften (mit Ausnahme der Referenz) ein. Ich muss also manchmal einen Typecast verwenden und manchmal nicht:
Delphi-Quellcode:
MyDog.Owner.Schimpfen;
if MyDog <> TDogOwner(MyDog.Owner).Dog Then
  Writeln('The dog was stolen');
Welche Klasse sollte man denn verwenden, wenn man einen Hundebesitzer instantiieren will? Das ist nicht klar, weil zwei Klassen existieren, wo eine reicht.

Dient alles nicht der Übersichtlichkeit.

Der schöne Günther 26. Aug 2013 21:44

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

Zitat von jaenicke (Beitrag 1226306)
Wir benutzen normalerweise auch eine Datei pro Interface und eine pro Klasse. Mit der Lösung funktioniert das problemlos, egal ob mit Interface oder Klasse

Das ist, zumindest bislang, auch mein Stil 8-)

Und ich glaube auch generell in der VCL, denn irgendwie haben 3/4 aller Klassen ein "Custom" im Namen, alle die es nicht haben, sind meist abstrakt :-D

Ich bin in meinen ersten Delphi-Monaten bislang nie in die Lage gekommen, Interfaces anstatt abstrakter Klassen zu nehmen. Irgendeine konkrete Teilimplementierung ließ sich bislang immer in eine Oberklasse herausziehen. Aber wie gesagt, ob das obendrüber jetzt nun ein Interface oder eine Klasse ist kümmert ja niemanden.



Zitat:

Zitat von Furtbichler (Beitrag 1226317)
TDogOwner führt keine neuen Methoden oder Eigenschaften (mit Ausnahme der Referenz) ein. Ich muss also manchmal einen Typecast verwenden und manchmal nicht

Moment. Ich dachte, das sei nur ein Tippfehler und soll heißen
Delphi-Quellcode:
TCustomDogOwner = class(TDogOwner)
?
Dann markiere ich den TDogOwner noch als abstrakt damit mir keiner einen instanziiert.


Zitat:

Zitat von Furtbichler (Beitrag 1226317)
Delphi-Quellcode:
if MyDog <> TDogOwner(MyDog.Owner).Dog Then
  Writeln('The dog was stolen');

:mrgreen:

Lemmy 26. Aug 2013 21:54

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

Zitat von jaenicke (Beitrag 1226306)
Eine andere Lösung ist eben die von mir angesprochene. Die bevorzuge ich eher. Wir benutzen normalerweise auch eine Datei pro Interface und eine pro Klasse. Mit der Lösung funktioniert das problemlos, egal ob mit Interface oder Klasse:


Du hast völlig recht und ist sicherlich besser als einen ClassHelper zu verwenden. Ich vermeide so was inzwischen allein für die Auflösung von zirkulären Beziehungen eine weitere Vererbungshierarchie einzuführen.

In einem größeren Projekt in dem ich das Businessmodell erstellt habe, fand ich am Ende im Basisframework (das aus ca. 6 Vererbungsebenen bestand) allein 2 Ebenen die ich nur für die Auflösung der zirkulären Referenzen eingeführt hatte. Am Ende waren die beiden Ebenen aber mit Funktionalität so erweitert worden (durch Kollegen), dass die nicht mehr so einfach zu entfernen waren.

Interfaces bieten hier die Möglichkeit einen deutlich lockeren Verbund zu erstellen. Durch die Maßgabe gegen das Interface zu implementieren und nicht gegen eine Implementierung kann ich die Implementierung auch jederzeit austauschen, z.B. um Tests durchzuführen...

Grüße

Namenloser 26. Aug 2013 22:31

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

Zitat von Der schöne Günther (Beitrag 1226204)
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?

Wenn eine Funktion nicht benutzt wird, landet sie auch nicht in der Exe. Zumindest wenn man vom Quellcode kompiliert, bei DCUs bin ich mir nicht ganz sicher.

Ansonsten, ich habe gerade mal nachgeschaut, meine einfachsten mit Delphi 2006 kompilierten Programme (also schon etwas mehr als eine leere Anwendung) sind in der Regel so 500 – 600kb groß.

Was unter neueren Delphis die Kompilate so aufbläht, ist wohl vor allem die erweiterte RTTI.

Furtbichler 26. Aug 2013 22:38

AW: Class Helper als Lösung für zirkuläre Unit-Referenzen
 
Ach macht doch wat ihr wollt :mrgreen:

jaenicke 27. Aug 2013 05:10

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

Zitat von Lemmy (Beitrag 1226322)
Interfaces bieten hier die Möglichkeit einen deutlich lockeren Verbund zu erstellen. Durch die Maßgabe gegen das Interface zu implementieren und nicht gegen eine Implementierung kann ich die Implementierung auch jederzeit austauschen, z.B. um Tests durchzuführen...

Klar, die Vorteile von Interfaces gibt es, aber solange man Interfaces nicht in die gleiche Datei packt, hat man das gleiche Problem. Bei Interfaces kommt deshalb das gleiche Schema in Frage.

Ich selbst habe das bisher eigentlich immer ohne Kreuzreferenz lösen können.

Zitat:

Zitat von Der schöne Günther (Beitrag 1226319)
Moment. Ich dachte, das sei nur ein Tippfehler und soll heißen
Delphi-Quellcode:
TCustomDogOwner = class(TDogOwner)
?

Schau mal in den VCL Quelltexten noch einmal. ;-)
Custom ist die Basis für konkrete Ableitungen, die dann nicht mehr das Custom drin haben. Zum Beispiel:
Delphi-Quellcode:
  TCustomEdit = class(TWinControl)
  TCustomMemo = class(TCustomEdit)

  TEdit = class(TCustomEdit)
  TMemo = class(TCustomMemo)

JamesTKirk 27. Aug 2013 05:33

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

Zitat von NamenLozer (Beitrag 1226329)
Zitat:

Zitat von Der schöne Günther (Beitrag 1226204)
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?

Wenn eine Funktion nicht benutzt wird, landet sie auch nicht in der Exe. Zumindest wenn man vom Quellcode kompiliert, bei DCUs bin ich mir nicht ganz sicher.

Ja, funktioniert auch bei DCUs. Hatte schon oft, dass in unserem vorkompiliertem Code Funktionen keine blauen Punkte hatten und ergo nicht Bestandteil der Executable waren. Nennt sich übrigens Smartlinking das Verfahren.

Gruß,
Sven

Furtbichler 27. Aug 2013 06:44

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

Zitat von jaenicke (Beitrag 1226339)
aber solange man Interfaces nicht in die gleiche Datei packt, hat man das gleiche Problem.

Und wenn man sie in eine Datei packt (bzw. nur die voneinander abhängigen Interfaces), dann eben nicht. :wink: Es ist nun einmal eine Spracheigenschaft von Delphi, mehrere Klassen in einer Datei halten zu können um damit bestimmte Dinge zu regeln (private vs. strict private und eben Forward-Deklarationen).

Der klassische Ansatz ist die Forward-Deklaration:
Delphi-Quellcode:
Type
  THund = Class;

  THundebesitzer = Class
    Property Hund : THund...
  end;
 
  THund = Class
    property Besitzer : THundebesitzer;
  End;
Und eigentlich ist das sehr schön lesbar, denn sobald man auf eine Forward-Deklaration stößt, weiß man, das hier Kreise im Spiel sind.

Im Übrigen löst die VCL das Problem der zirkulären Referenzen ja nicht, weil sie TCustomXXX und TXXX implementiert, sondern indem sie die Klassen in einer Datei vorhält (soweit ich das überschaue, jedenfalls).

jaenicke 27. Aug 2013 07:37

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

Zitat von Furtbichler (Beitrag 1226344)
Und eigentlich ist das sehr schön lesbar, denn sobald man auf eine Forward-Deklaration stößt, weiß man, das hier Kreise im Spiel sind.

Lesbar schon, gut testbar weniger.


Alle Zeitangaben in WEZ +1. Es ist jetzt 09:02 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