AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Wie töte ich ein TAggregatedObject?

Ein Thema von Der schöne Günther · begonnen am 10. Dez 2013 · letzter Beitrag vom 10. Dez 2013
Antwort Antwort
Der schöne Günther

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

Wie töte ich ein TAggregatedObject?

  Alt 10. Dez 2013, 12:17
Delphi-Version: XE5
Denksport-Aufgabe am frühen Morgen.

Ich habe eine Klasse (nennen wir sie TMonsterklasse ) welche die Realisierung eines Interfaces (IMyInterface ) an eine andere Klasse (TMyInterfaceDelegate ) delegieren soll.

Je nachdem was man haben möchte, leitet man TMyInterfaceDelegate nun am besten von TAggregatedObject oder TContainedObject ab. Zur Erinnerung: Ersteres leitet alle _QueryInterface -Anfragen auf sich wieder an die TMonsterklasse zurück, TContainedObject nicht. Beide reichen aber die ARC-Methoden _AddRef und _Release an TMonsterklasse zurück.

Und wo ist nun die Frage? Folgendes: Im Destruktor meiner TMonsterklasse bekomme ich das aggregierte TMyInterfaceDelegate nicht freigegeben da ich es mir in TMonsterklasse nur als Interface-Referenz vom Typ IMyInterface merke.
Das einmal als Bild im Anhang und im Folgenden als Delphicode:

Delphi-Quellcode:
IMyInterface = interface
   ['{62EB99E2-5762-41C2-AF46-D612B9BD5C01}']
   procedure someProc();
end;

//TMyInterfaceDelegate = class(TContainedObject, ISubInterface)
TMyInterfaceDelegate = class(TAggregatedObject, IMyInterface)
   public
      // controller wäre dann eine TMonsterklasse-Instanz
      constructor Create(const controller: IInterface);
      destructor Destroy(); override;

      procedure someProc();
end;

// Diese Klasse delegiert die Realisierung von IMyInterface an
// ein aggregiertes TMyInterfaceDelegate-Objekt
TMonsterklasse = class(TInterfacedObject, IMyInterface)
   private var
      mySubInterfaceDelegate: IMyInterface;

   private
      function getMyInterfaceDelegate(): IMyInterface;

   public
      constructor Create();
      destructor Destroy(); override;

   // Interface Delegation
   protected property myInterfaceRealization: IMyInterface
   read getMyInterfaceDelegate implements IMyInterface;
end;
Interessante Teile der Implementation sind

Delphi-Quellcode:
function TMonsterklasse.getMyInterfaceDelegate(): IMyInterface;
begin
   if not Assigned(mySubInterfaceDelegate) then begin
      mySubInterfaceDelegate := TMyInterfaceDelegate.Create(self);
      // _AddRef auf myRealizer wird wieder an mich selbst delegiert!
      // Da der RefCount sonst niemals Null wird, erniedrige ich ihn
      // hier künstlich wieder um eins.
      self._Release();
   
   end;

   Result := mySubInterfaceDelegate;
end;

destructor TMonsterklasse.Destroy();
begin
   // Wie zerstöre ich nun mySubInterfaceDelegate?
   
   // An den Referenzzähler komme ich nicht dran
   
   // Ist es ein TContainedObject kann ich noch zu einer Klassenreferenz
   // casten und dann den Destruktor aufrufen. Aber bei TAggregatedObject
   // wird _QueryInterface an mich delegiert und ich kann nichtmals
   // zu einer Klasse casten...
   (mySubInterfaceDelegate as TContainedObject).Free();
end;
Wie man sieht, alles andere als schön. Ich möchte allerdings vermeiden, meine Referenz auf das aggregierte Objekt in der ganz konkreten Klasse (also vom Typ TMyInterfaceDelegate ) zu hinterlegen. Meine Idee wäre, die Referenz immerhin noch vom Typ TAggregatedObject zu halten. Die obige Implementation verkürzt sich somit auf
Delphi-Quellcode:
destructor TMonsterklasse.Destroy();
begin
   mySubInterfaceDelegate.Free();
end;

function TMonsterklasse.getMyInterfaceDelegate(): IMyInterface;
begin
   if not Assigned(mySubInterfaceDelegate) then
      mySubInterfaceDelegate := TMyInterfaceDelegate.Create(self);

   if not Supports(mySubInterfaceDelegate, IMyInterface, Result) then
      raise Exception.Create('Ich habe keine Ahnung was dann geschehen soll');
end;
Hier fühle ich mich aber mit der letzten Methode nicht wohl. Aber immerhin funktioniert es und alles wird auch ordentlich freigegeben. Kann mir jemand gut zureden, dass ich alles richtig mache? Oder kann man etwas anders lösen?
Miniaturansicht angehängter Grafiken
klassendiagramm_mit-weissem-hintergrund.png  

Geändert von Der schöne Günther (10. Dez 2013 um 12:21 Uhr) Grund: Klassendiagramm nicht mehr transparent
  Mit Zitat antworten Zitat
Benutzerbild von Uwe Raabe
Uwe Raabe

Registriert seit: 20. Jan 2006
Ort: Lübbecke
11.475 Beiträge
 
Delphi 12 Athens
 
#2

AW: Wie töte ich ein TAggregatedObject?

  Alt 10. Dez 2013, 14:33
Da TAggregatedObject nicht über die Referenzzählung freigegeben wird, spricht nichts gegen eine lokale Instanz der entsprechenden Klasse innerhalb deiner Monster-Klasse. Da die Monster-Klasse für die Freigabe der Instanz verantwortlich ist, entspricht das auch dem gewünschten Design.

Gegen eine Speicherung in einer Interface-Variable spricht, daß du dir damit eine unerwünschte Referenzzählung einhandelst, die ein Freigeben der Monster-Instanz eigentlich verhindert, was du ja auch mit dem self._Release ausgleichst (müffelt halt etwas).

Es ist auch durchaus korrekt, wenn du das Feld als mySubInterfaceDelegate: TMyInterfaceDelegate deklarierst. Damit kannst du deine getMyInterfaceDelegate Funktion nämlich fasst so belassen wie sie ist.

Grundsätzlich (sprich: ich kenne bisher keine Ausnahme) kann man sagen, daß bei zusammengesetzten Objekten mit TAggregatedObject und TContainedObject immer die passenden Klassen als lokale Felder deklariert werden sollten.

Der explizite _Release -Aufruf sollte bei einem selbst schon eine Warnung auslösen daß etwas nicht korrekt ist. In Delphi werden _AddRef und _Release eher selten direkt aufgerufen.
Uwe Raabe
Certified Delphi Master Developer
Embarcadero MVP
Blog: The Art of Delphi Programming
  Mit Zitat antworten Zitat
Der schöne Günther

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

AW: Wie töte ich ein TAggregatedObject?

  Alt 10. Dez 2013, 18:54
Ich wusste doch, auf den Spezialisten zu diesem Gebiet kann ich mich verlassen

Dann ist schon mal sehr sicher, dass man die einzelnen Bausteine als Klassenreferenzen, nicht als Interface anspricht.

Meine Motivation, als Typ eine Basisklasse möglichst weit oben (z.B. TAggregatedObject ) zu nehmen war, dass ich so auch schnell die Klasse austauschen kann bzw. einfach etwas anderes aus einer Factory kommt.

Andererseits sind es ja nur sehr wenige Stellen die man anfassen müsste, wollte man die verwendete Klasse komplett austauschen. Ich werde da mal drüber schlafen...

Vielen Dank für die Antwort
  Mit Zitat antworten Zitat
Antwort Antwort

 

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 03:04 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