Delphi: How delegate interface implementation to child object?

前端 未结 2 1383
春和景丽
春和景丽 2020-12-09 20:13

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

相关标签:
2条回答
  • 2020-12-09 20:36

    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.
    
    0 讨论(0)
  • 2020-12-09 20:36

    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.

    • Delegating to interface type
    • Delegating to class Type
    • Method aliasing

    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:

    • Ensure the objects you're delegating to have an appropriate lifetime.
    • Be sure to hold a reference to the delegatee. Remember that interfaces are reference counted, and will be destroyed as soon as the count drops to zero. This may actually have been the cause of your headaches.
    0 讨论(0)
提交回复
热议问题