Poetischer Titel. Und vielleicht passt es doch besser speziell nach Object-Pascal / Delphi-Language. Ich bin nicht sicher.
Mir ist heute etwas sehr interessantes aufgefallen: Nehmen wir folgenden Codeschnipsel:
Delphi-Quellcode:
IMessageInterface = interface
['{F3B8FE2F-C64F-4E5B-B731-6ABB09CDF4E7}']
procedure ShowMessage();
end;
TMessager = class(TInterfacedObject, IMessageInterface)
private var
myMessage: String;
public
constructor Create(const myMessage: String);
procedure showMessage();
end;
TContainerClass = class(TInterfacedObject, IMessageInterface)
private
function getMessageDelegate(): TMessager;
private var
messageDelegate: TMessager;
// Delegation: IMessageInterface
property messageInterface: TMessager
read getmessageDelegate implements IMessageInterface;
public
constructor Create();
procedure createNewDelegateInstance();
end;
Was passiert? Wir haben eine Klasse
TContainerClass
welche sich (in ihrem Konstruktor) eine
TMessager
-Instanz erstellt und die
ShowMessage
-Methode an diese abschiebt.
Die Methode
createNewDelegateInstance()
zerstört diese Instanz und erstellt eine neue. Unterscheiden kann man beide: Die erste gibt "Erste", die neue Instanz würde "Zweite" auf den Bildschirm ausgeben.
Soweit alles klar? Interessant finde ich nun, was man tatsächlich bekommt, wenn man sich von
TContainerClass
nun eine
IMessageInterface
-Referenz holt. Führen wir folgendes aus:
Delphi-Quellcode:
var
container: TContainerClass;
messageIntf: IMessageInterface;
begin
container := TContainerClass.Create();
Supports(container, IMessageInterface, messageIntf);
messageIntf.ShowMessage();
sieht man erwartungsgemäß "Erster" auf dem Bildschirm. Schaut man im Debugger aber auf die Variable
messageIntf
sieht man dort:
Code:
Name Wert
messageIntf TMessager($286B964) as IMessageInterface
. Die Referenz hat überhaupt keinen Bezug mehr zu der
TContainerClass
-Instanz, wir haben hier direkt die
TMessager
-Instanz!
Warum ist das so? Hat das bestimmte Gründe?
Jetzt kommt der Punkt, der mich verwirrt: Ändern wir von
TConainerClass
die Methode
getMessageDelegate()
dass sie kein Interface (
IMessageInterface
) sondern eine Klasse (
TMessager
) zurückgibt, läuft die Sache ganz anders ab:
Nach dem Aufruf von
Supports(..)
haben wir in unserer Interface-Variable eine Referenz auf die
TContainerClass
-Instanz! Erst durch den folgenden Aufruf von
ShowMessage
wird die
getMessageDelegate
-Methode abgearbeitet, die TMessager-Instanz zurückgegeben und auf ihr schließlich
ShowMessage
aufgerufen.
Konkret bedeutet das:
Führen wir also folgendes (zwei Zeilen mehr) aus:
Delphi-Quellcode:
var
container: TContainerClass;
messageIntf: IMessageInterface;
begin
container := TContainerClass.Create();
Supports(container, IMessageInterface, messageIntf);
messageIntf.ShowMessage();
container.createNewDelegateInstance();
messageIntf.ShowMessage();
Ergibt das im ersten Fall die Ausgabe
- Erster
- Erster
Während im zweiten Fall
- Erster
- Zweiter
ausgegeben wird.
Liest noch jemand mit? Meine Schlussfolgerung, was das
konkret bedeutet: Nehmen wir an, äußere Umstände zwingen mein TContainerClass-Objekt nun dazu, dass es keine Message senden kann. Es bekommt von nirgendwo einen
TMessager
her. Alle sind ausverkauft. Als Ersatz liefert es nun ein Nullobjekt, einen Dummy.
Im ersten Fall lässt sich das nicht machen. Derjenige, der sich zum Zeitpunkt als ich nichts im Angebot hatte einmal eine Interface-Referenz geholt hatte bekommt nie mit, wenn ich denn einmal wieder ein vernünftiges
TMessager
-Objekt habe: Seine Referenz wird immer auf den Dummy zeigen.
Im zweiten Fall geht das - Nur muss sich mein Nullobjekt zwangsweise von TMessager ableiten. Das hat zahlreiche Probleme und Nachteile.
Folglich: Interface-Delegation an aggregierte Objekte. Und der Delegat ändert sich später. Das kann nicht gut gehen, oder? Oder verbeiße ich mich hier nur zu sehr in das "implements" Keyword? Würde ich für alle Interface-Methoden eine eigene Method Resolution Clause nehmen würde man alle Probleme umgehen. Aber hässlich das ist doch potthässlich. Ist das vom Konzept her insgesamt ein No-Go?