i have an object which delegates implementation of a particularly complex interface to a child object. This is exactly i think is the job of TAggregat
You have to add a field instance for the created child object:
type
TRobot = class(TInterfacedObject, IStream)
private
FStream: TRobotStream;
function GetStream: IStream;
public
property Stream: IStream read GetStream implements IStream;
end;
destructor TRobot.Destroy;
begin
FStream.Free;
inherited;
end;
function TRobot.GetStream: IStream;
begin
if FStream = nil then
FStream := TRobotStream.Create(Self);
result := FStream;
end;
Update TRobotStream should be derived from TAggregatedObject as you already guessed. The declaration should be:
type
TRobotStream = class(TAggregatedObject, IStream)
...
end;
It is not necessary to mention IUnknown.
In TRobot.GetStream the line result := FStream
does an implicite FStream as IStream
so writing this out isn't necessary either.
FStream has to be declared as TRobotStream and not as IStream so it can be destroyed when the TRobot instance is destroyed. Note: TAggregatedObject has no reference counting so the container has to take care of its lifetime.
Update (Delphi 5 code):
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, activex, comobj;
type
TForm1 = class(TForm)
Button1: TButton;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
private
procedure LoadRobotFromDatabase(rs: IStream);
public
end;
type
TRobotStream = class(TAggregatedObject, IStream)
public
{ IStream }
function Seek(dlibMove: Largeint; dwOrigin: Longint;
out libNewPosition: Largeint): HResult; stdcall;
function SetSize(libNewSize: Largeint): HResult; stdcall;
function CopyTo(stm: IStream; cb: Largeint; out cbRead: Largeint; out cbWritten: Largeint): HResult; stdcall;
function Commit(grfCommitFlags: Longint): HResult; stdcall;
function Revert: HResult; stdcall;
function LockRegion(libOffset: Largeint; cb: Largeint; dwLockType: Longint): HResult; stdcall;
function UnlockRegion(libOffset: Largeint; cb: Largeint; dwLockType: Longint): HResult; stdcall;
function Stat(out statstg: TStatStg; grfStatFlag: Longint): HResult; stdcall;
function Clone(out stm: IStream): HResult; stdcall;
function Read(pv: Pointer; cb: Longint; pcbRead: PLongint): HResult; stdcall;
function Write(pv: Pointer; cb: Longint; pcbWritten: PLongint): HResult; stdcall;
end;
type
TRobot = class(TInterfacedObject, IStream)
private
FStream: TRobotStream;
function GetStream: IStream;
public
destructor Destroy; override;
property Stream: IStream read GetStream implements IStream;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
rs: IStream;
begin
rs := TRobot.Create;
LoadRobotFromDatabase(rs); //dummy method, just to demonstrate we use the stream
rs := nil;
end;
procedure TForm1.LoadRobotFromDatabase(rs: IStream);
begin
rs.Revert; //dummy method call, just to prove we can call it
end;
function TRobotStream.Clone(out stm: IStream): HResult;
begin
end;
function TRobotStream.Commit(grfCommitFlags: Integer): HResult;
begin
end;
function TRobotStream.CopyTo(stm: IStream; cb: Largeint; out cbRead, cbWritten: Largeint): HResult;
begin
end;
function TRobotStream.LockRegion(libOffset, cb: Largeint; dwLockType: Integer): HResult;
begin
end;
function TRobotStream.Read(pv: Pointer; cb: Integer; pcbRead: PLongint): HResult;
begin
end;
function TRobotStream.Revert: HResult;
begin
end;
function TRobotStream.Seek(dlibMove: Largeint; dwOrigin: Integer;
out libNewPosition: Largeint): HResult;
begin
end;
function TRobotStream.SetSize(libNewSize: Largeint): HResult;
begin
end;
function TRobotStream.Stat(out statstg: TStatStg; grfStatFlag: Integer): HResult;
begin
end;
function TRobotStream.UnlockRegion(libOffset, cb: Largeint; dwLockType: Integer): HResult;
begin
end;
function TRobotStream.Write(pv: Pointer; cb: Integer; pcbWritten: PLongint): HResult;
begin
end;
destructor TRobot.Destroy;
begin
FStream.Free;
inherited;
end;
function TRobot.GetStream: IStream;
begin
if FStream = nil then
FStream := TRobotStream.Create(Self);
result := FStream;
end;
end.
There is no need for your class that does the delegation to inherit from any particular class. You could inherit from TObject provided the appropriate methods have been implemented. I'll keep things simple and illustrate using TInterfacedObject which provides the 3 core methods which you have already identified.
Also, you should not need TRobotStream = class(TAggregatedObject, IUnknown, IStream)
. You could instead simply declare that IStream inherits from IUnknown. By the way, I always give my interfaces a GUID (Press the conbination Ctrl+Shift+G).
There are a number of different approaches and techniques that can be applied depending on your particular needs.
The simplest delegation is by interface.
TRobotStream = class(TinterfacedObject, IStream)
TRobot = class(TInterfacedObject, IStream)
private
//The delegator delegates the implementations of IStream to the child object.
//Ensure the child object is created at an appropriate time before it is used.
FRobotStream: IStream;
property RobotStream: IStream read FRobotStream implements IStream;
end;
There are perhaps a few thing to watch out for: