Delphi 介面(interface)定義行為契約(方法簽名),實現類(class)提供具體行為。同一介面變量可在運行時綁定不同實現,實現運行時多態,且無需繼承關係——這就是「面向介面編程」的精髓。
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.
TInterfacedObject:引用計數基類IInterface 的 _AddRef/_Release/QueryInterface。_AddRef,離開作用域/賦 nil/重新賦值時自動 _Release。_Release 歸零時調用 Destroy。Calc := TAdder.Create; // RefCount=1
Calc := TMultiplier.Create; // 1) TAdder._Release → RefCount=0 → Destroy
// 2) TMultiplier._AddRef → RefCount=1
這是 Delphi 介面區別於對象變量的核心差異:對象變量重新賦值會導致洩漏,介面變量自動清理舊引用。
介面不能聲明字段。若需保持狀態(如計算歷史、配置參數),定義在實現類的私有字段中:
type
TConfigurableAdder = class(TInterfacedObject, ICalculator)
private
FOffset: Integer; // 狀態歸屬於具體實現
public
constructor Create(AOffset: Integer);
function Calculate(a, b: Integer): Integer;
end;
as 轉換與跨模塊['{GUID}'] 才能用 Obj as ICalculator 安全轉換(失敗拋異常而非 AV)。QueryInterface 失敗。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 自動引用計數,你獲得了:無繼承耦合的多態、自動生命週期管理、運行時熱插拔實現、跨模塊二進制兼容。這比傳統虛方法多態更輕量、更靈活,是構建插件系統、策略模式、依賴注入容器的基石。