首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> Delphi

Delphi 介面編程 (5):同一介面、不同實現——多態的本質

     編輯:Delphi

核心思想:介面隔離「契約」與「實現」

Delphi 介面(interface)定義行為契約(方法簽名),實現類(class)提供具體行為。同一介面變量可在運行時綁定不同實現,實現運行時多態,且無需繼承關係——這就是「面向介面編程」的精髓。

完整代碼示範:加法 vs 乘法策略切換

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  // 1. 介面定義:純契約,無字段、無實現
  ICalculator = interface
    ['{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}'] // GUID 可選,有則支援 as 轉換
    function Calculate(a, b: Integer): Integer;
  end;

  // 2. 實現 A:加法策略
  TAdder = class(TInterfacedObject, ICalculator)
  public
    function Calculate(a, b: Integer): Integer;
    destructor Destroy; override;
  end;

  // 3. 實現 B:乘法策略
  TMultiplier = class(TInterfacedObject, ICalculator)
  public
    function Calculate(a, b: Integer): Integer;
    destructor Destroy; override;
  end;

  TForm1 = class(TForm)
    btnTest: TButton;
    procedure btnTestClick(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TAdder }
function TAdder.Calculate(a, b: Integer): Integer;
begin
  Result := a + b;
end;

destructor TAdder.Destroy;
begin
  ShowMessage('TAdder 銷毀');
  inherited;
end;

{ TMultiplier }
function TMultiplier.Calculate(a, b: Integer): Integer;
begin
  Result := a * b;
end;

destructor TMultiplier.Destroy;
begin
  ShowMessage('TMultiplier 銷毀');
  inherited;
end;

{ TForm1 }
procedure TForm1.btnTestClick(Sender: TObject);
var
  Calc: ICalculator;
begin
  // 綁定加法實現
  Calc := TAdder.Create;
  ShowMessage('9 + 9 = ' + IntToStr(Calc.Calculate(9, 9))); // 18

  // 關鍵:重新賦值自動釋放舊對象 (TAdder.Destroy 觸發)
  Calc := TMultiplier.Create;
  ShowMessage('9 * 9 = ' + IntToStr(Calc.Calculate(9, 9))); // 81

  // 過程結束,Calc 離開作用域,自動釋放 TMultiplier
end;

end.

關鍵機制深度解析

1. TInterfacedObject:引用計數基類

  • 實現 IInterface 的 _AddRef/_Release/QueryInterface。
  • 介面變量賦值時自動 _AddRef,離開作用域/賦 nil/重新賦值時自動 _Release。
  • _Release 歸零時調用 Destroy。

2. 介面變量賦值的「隱式釋放」語義

Calc := TAdder.Create;   // RefCount=1
Calc := TMultiplier.Create; // 1) TAdder._Release → RefCount=0 → Destroy
                            // 2) TMultiplier._AddRef → RefCount=1

這是 Delphi 介面區別於對象變量的核心差異:對象變量重新賦值會導致洩漏,介面變量自動清理舊引用。

3. 介面無狀態,狀態在實現類

介面不能聲明字段。若需保持狀態(如計算歷史、配置參數),定義在實現類的私有字段中:

type
  TConfigurableAdder = class(TInterfacedObject, ICalculator)
  private
    FOffset: Integer; // 狀態歸屬於具體實現
  public
    constructor Create(AOffset: Integer);
    function Calculate(a, b: Integer): Integer;
  end;

4. GUID 的作用:支援 as 轉換與跨模塊

  • 介面聲明帶 ['{GUID}'] 才能用 Obj as ICalculator 安全轉換(失敗拋異常而非 AV)。
  • 跨包/跨 DLL 共享介面時,GUID 必須一致,否則 QueryInterface 失敗。
  • 內部單元介面可省略 GUID,但建議統一加上(Ctrl+Shift+G 生成)。

實戰模式:策略模式 + 介面工廠

type
  ICalculatorFactory = interface
    function CreateCalculator(const AType: string): ICalculator;
  end;

  TCalculatorFactory = class(TInterfacedObject, ICalculatorFactory)
  public
    function CreateCalculator(const AType: string): ICalculator;
  end;

function TCalculatorFactory.CreateCalculator(const AType: string): ICalculator;
begin
  if SameText(AType, 'Add') then
    Result := TAdder.Create
  else if SameText(AType, 'Mul') then
    Result := TMultiplier.Create
  else
    raise Exception.Create('未知計算器類型: ' + AType);
end;

避坑指南

錯誤後果正確做法
實現類繼承 TObject 而非 TInterfacedObject介面變量賦值不增減引用,離開作用域不釋放 → 洩漏必須繼承 TInterfacedObject 或手動實現 _AddRef/_Release
介面方法用 virtual/override介面方法隱式 virtual,無需/不能加修飾符介面聲明不加 virtual,實現類可加 override 但非必需
介面聲明字段編譯錯誤字段只能在類中
手動 Free 介面變量引用的對象雙重釋放 → AV絕不手動 Free,讓引用計數管理
在構造函數中將 Self 賦給介面變量構造未完成 RefCount=0,臨時介面變量離開作用域釋放 → 對象在構造中被銷毀構造完成後再賦值,或用工廠函數返回介面

總結

「同一介面、不同實現」是 Delphi 介面編程的多態核心。配合 TInterfacedObject 自動引用計數,你獲得了:無繼承耦合的多態、自動生命週期管理、運行時熱插拔實現、跨模塊二進制兼容。這比傳統虛方法多態更輕量、更靈活,是構建插件系統、策略模式、依賴注入容器的基石。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved