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:
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.
Ok, nachdem ich nun alle total verwirrt habe, ist vielleicht ein Beispiel
angesagt:
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.
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 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 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;
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.
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
|