twm's homepage logo
Von einem, der auszog die Heimat schätzen zu lernen ...
Object Pascal Interfaces die Zweite
Deutsch English
Google
Search dummzeuch.de
Search WWW

Einleitung

Wie schon in meinem anderen Artikel über Interfaces erwähnt, ist die einfachste Möglichkeit ein Interface zu implementieren eine von TInterfacedObject abgeleitete Klasse, so dass man sich die Implementation der AddRef, Release etc. Methoden sparen kann.

Manchmal ist dies aber nicht möglich, z.B. wenn man eine bereits existierende Klasse zur Implementation verwenden will, die nicht direkt oder indirekt von TInterfacedObject abgeleitet ist und auch nicht dahingehend geändert werden kann. Oder aber man will das Referenz-Counting nicht verwenden, da eine bereits existierende Instanz eines Objekts auch ein Interface implementieren soll, aber nicht zerstört werden soll, wenn es keine Interface-Referenz mehr gibt.

Auch für diese Fälle bietet Delphi eine Lösung:

Delegation

Vereinfacht ausgedrückt überläßt eine Klasse die Implementation eines Interfaces einer anderen Klasse oder einem anderen Interface, die einzige Voraussetzung ist, dass diese andere Klasse eine Property ist.

Beispiel

Ok, nachdem ich nun alle total verwirrt habe, ist vielleicht ein Beispiel angesagt:

Gegeben

Man nehme folgendes Interface

                      
type
  ISomeInterface = interface
    procedure Method1;
    procedure Method2;
  end;
                    

sowie die Klasse

                    
type
  TSomeClass = class(TSomeOtherClass)
    procedure Method1;
    procedure Method2;
  end;
                  

mit der entsprechenden Implementation.

Gesucht

Wie unschwer festzustellen ist, würde sich TSomeClass wunderbar zur Implementation von ISomeInterface eignen, wenn man sie von TInterfacedObject ableiten oder ihr die noch fehlenden Methoden anderweitig beibringen könnte.

Leider ist ersteres nicht möglich, da Delphi keine multiple Vererbung unterstützt (Hah, ich sehe die C++-Fans schon hämisch grinsen. Die Java-Fraktion sei daran erinnert, dass Java das auch nicht kann. Java und Delphi sind sich überhaupt sehr ähnlich, wenn SUN sich bloß nicht ausgerechnet das kryptische C++ als Vorlage für die Syntax ausgesucht hätte...).

Die Visual Basic Lösung

Die Implementation von AddRef und Konsorten ist zwar nicht wirklich schwierig (man könnte sie z.B. von TInterfacedObject kopieren, auch wenn das doch ein wenig zu sehr an die typische Visual Basic "Programmierung" erinnert), aber wir wollen uns das bisher noch übersichtliche Interface von TSomeClass nicht damit unnötig aufblähen.

Die Delphi Lösung

Die Alternative könnte wie folgt aussehen:

Man deklariere eine von TInterfacedObject abgeleitete Wrapper-Klasse, welche die lästige Basisimplementation von IUnknown übernimmt und die darüber hinausgehende Implementation von ISomeInterface an TSomeClass delegiert:

                  
type
  TSomeWrapper = class(TInterfacedObject, ISomeInterface)
  protected
    fSomeClass: TSomeClass;
    property SomeClass: TSomeClass read fSomeClass implements ISomeInterface;
  public
    constructor Create(_SomeClass: TSomeClass);
    destructor Destroy; override;
  end;

...

constructor TSomeWrapper.Create(_SomeClass: TSomeClass);
begin
  inherited Create;
  fSomeClass := _SomeClass;
end;

destructor TSomeWrapper.Destroy;
begin
  fSomeClass.Free;
  inherited;
end;
                

Mit Anwendung

Benutzt wird dieses Objekt wie folgt:

                
var
  SomeInt: ISomeInterface;
...
  SomeInt := TSomeWmapper.Create(TSomeClass.Create);
  SomeInt.Method1;
...
              

Man erzeugt also eine Instanz von TSomeWrapper, übergibt ihr eine ebenfalls frisch erzeugte Instanz von TSomeClass, und verwendet sie als ob es sich um eine auf magische Weise auch von TInterfacedObject abgeleitete Version von TSomeClass handelte, weist sie z.B. auf eine Variable vom Typ ISomeInterface zu. Reference-Counting natürlich inklusive.

Reference Counting OFF

Aber wenn man nun kein Reference-Counting will? Ganz einfach, dann läßt man beim Destructor von TSomeWrapper einfach die Freigabe von fSomeObject weg, und schon ist folgendes möglich:

Man kann die Deklaration von TSomeWrapper in die Implementation Section der Unit verlegen und verpasst TSomeClass eine neue Methode:

              
type
  TSomeClass = class(TSomeOtherClass)
    procedure Method1;
    procedure Method2;
    function AsSomeInterface: ISomeInterface;
  end;

...

function TSomeClass.AsSomeInterface: ISomeInterface;
begin
  Result := TSomeWrapper.Create(self);
end;

...
var
  SomeInt: ISomeInterface;

  SomeInt := SomeExistingInstance.AsSomeInterface;
            

Man kann also für eine bereits existierende Instanz von TSomeClass ein Interface erzeugen lassen, ohne dass diese Instanz automatisch freigegeben wird, wenn das Interface out of Scope geht (hat mal jemand eine gute deutsche Übersetzung für "out of Scope (gehen)"?), denn dann wird nur die Instanz von TSomeWrapper freigegeben, nicht jedoch die darin referenzierte Instanz von TSomeClass.



This document was generated using AFT v5.096

letzte Änderung: 2012-10-14 twm
Post to del.icio.us Best Viewed With Open EyesValid XHTML 1.0!