Dieses Pattern soll einen Weg aufzeigen, wie man auch mit Delphi eine Art Garbage Collection bzw. Managed Objects implementieren kann. Was bedeutet das? Gaaaanz einfach: das Ziel ist es, dass eine Instanziertes Objekt automatisch freigegeben (destroyed) wird, sobald es "out of Scope" (ausserhalb des Gültikkeitsbereiches) ist.

Hierzu verwenden wir Interfaces. Das coole an Interfaces (u.a.) ist, dass sie über einen Reference-Counter verfügen und in Delphi als "managed" gelten. D.h. jedesmal, wenn man eine Klasse bzw. das Interface davon referenziert, wird ein "verwendet Counter" mitgezählt. Fällt die Referenzierung aus dem Scope (Gültigkeitsbereich) wird der Counter wieder runter gezählt. Ist der Count 0 wird das Objekt freigegeben.

Ok, Schluss mit quatschen - jetzt gibts Code. Schauts Euch an, und nehmt's als Idee auf

Ohne Interfaces

Die meissten von Euch kennen wohl Konstrukte wie das folgende nur zu gut:

type 
  TTestClass = class { Normale Klasse definieren } 
  public 
    destructor Destroy; override; 
  end; 

implementation

{ TTestClass } 
destructor TTestClass.Destroy; 
begin 
  ShowMessage('Object destroyed.'); 
  inherited; 
end; 

{ TForm1 } 
procedure TForm1.Button1Click(Sender: TObject); 
var 
  TestObject : TTestClass; 
begin 
  TestObject := TTestClass.Create; 
  try 
    { Do something } 
  finally 
    TestObject.Free; 
  end; 
end;

Mit Interfaces

Ok, nun lasst uns das obige Beispiel etwas abändern, sodass wir da mit Interfaces operieren. Wie gesagt, das Ziel ist es, dass wir uns nicht mehr selbst um den Free zu kümmern brauchen.

type 
  TTestClass = class(TInterfacedObject) 
  public 
    destructor Destroy; override; 
  end; 

implementation 

{ TTestClass } 
destructor TTestClass.Destroy; 
begin 
  ShowMessage('Object destroyed.'); 
  inherited; 
end; 

{ TForm1 } 
procedure TForm1.Button1Click(Sender: TObject); 
var 
  TestObject : IInterface;
begin 
  TestObject := TTestClass.Create;
  { Do something } 
end;

Interessant vor allem die Methode Button1Click(), welche keinen Free() mehr hat. Und so funktioniert's: TestObject ist neu eine Interface-Referenz. Als solche ist es von Delphi "gemanagt". Beim Create wird eine Referenz auf die Instanz gesetzt. Sobald das TestObject aus dem Scope (Gltigkeitsbereich) fällt, was beim END der Fall ist, wird die Referenz vom Delphi automatisch wieder "entfernt" und TestObject freigegeben, da es nun keine Referenz mehr auf das Objekt gibt.

Zu beachten:

  • Wenn TestObject ein Objekt wre, welches in der Methode Button1Click nur erstellt wird, aber an einem anderen "Ort" im Code wieder freigegeben werden soll, dann ist das nichts für Interfaces.
  • Wenn man nicht von einer Klasse ableiten kann, welche bereits ein Interface implementiert, wie oben z.B. von TInterfacedClass, dann muss man die Klasse von zusätzlich von einem Interface wie z.B. IInterface (ab Delphi 6) bzw. IUnknown (Delphi 5 und älter) ableiten und die drei Methoden _AddRef(), _ReleaseRef() und QueryInterface() implementieren. So kann man jede beliebige Klasse mit Referenz-Countern ausstatten.
  • Wenn man mit Interfaces zu arbeiten beginnt, laufen einige Dinge eben etwas anders ab (wie im obigen Beispiel zu sehen war). Entsprechend empfielt es sich auch Kommentare im Sourcecode zu machen, dass andere Entwickler dies auch wahrnehmen, da nicht jeder Delphi-Programmierer Interfaces aus dem Nähkstchen kennt.