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

Delphi 介面編程 (2):第一個例子——多介面實現與查詢

     編輯:Delphi

從零構建:一個類實現多介面

Delphi 介面編程的「Hello World」不是單介面,而是一個類實現多個介面,展示 QueryInterface 如何根據介面類型返回不同能力視圖——這正是 COM/介面設計的核心價值。

完整可運行代碼

unit Unit1;

interface

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

type
  // 介面 1:計算能力
  ICalculator = interface
    ['{C3D4E5F6-A7B8-9012-CDEF-345678901234}']
    function Add(a, b: Integer): Integer;
    function Mul(a, b: Integer): Integer;
  end;

  // 介面 2:顯示能力
  IDisplay = interface
    ['{D4E5F6A7-B8C9-0123-DEFA-456789012345}']
    procedure ShowMsg(const Msg: string);
    procedure ShowValue(const Caption: string; Value: Integer);
  end;

  // 單一類實現兩個介面
  TMyTool = class(TInterfacedObject, ICalculator, IDisplay)
  public
    function Add(a, b: Integer): Integer;
    function Mul(a, b: Integer): Integer;
    procedure ShowMsg(const Msg: string);
    procedure ShowValue(const Caption: string; Value: Integer);
  end;

  TForm1 = class(TForm)
    btnObj: TButton;      // 直接用對象調用
    btnCalc: TButton;     // 用 ICalculator
    btnDisp: TButton;     // 用 IDisplay
    btnBoth: TButton;     // 雙介面切換
    procedure btnObjClick(Sender: TObject);
    procedure btnCalcClick(Sender: TObject);
    procedure btnDispClick(Sender: TObject);
    procedure btnBothClick(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

{ TMyTool }
function TMyTool.Add(a, b: Integer): Integer;
begin
  ShowMessage('ICalculator.Add');
  Result := a + b;
end;

function TMyTool.Mul(a, b: Integer): Integer;
begin
  ShowMessage('ICalculator.Mul');
  Result := a * b;
end;

procedure TMyTool.ShowMsg(const Msg: string);
begin
  ShowMessage('IDisplay.ShowMsg: ' + Msg);
end;

procedure TMyTool.ShowValue(const Caption: string; Value: Integer);
begin
  ShowMessage(Caption + ': ' + IntToStr(Value));
end;

{ TForm1 }
procedure TForm1.btnObjClick(Sender: TObject);
var
  Obj: TMyTool;
begin
  Obj := TMyTool.Create;
  try
    Obj.Add(3, 5);
    Obj.Mul(3, 5);
    Obj.ShowMsg('對象直接調用');
    Obj.ShowValue('結果', 42);
  finally
    Obj.Free; // 對象變量必須手動 Free
  end;
end;

procedure TForm1.btnCalcClick(Sender: TObject);
var
  Calc: ICalculator;
begin
  Calc := TMyTool.Create; // 介面變量,自動引用計數
  ShowMessage('3+5=' + IntToStr(Calc.Add(3, 5)));
  ShowMessage('3*5=' + IntToStr(Calc.Mul(3, 5)));
  // Calc 只能訪問 ICalculator 方法,IDisplay 不可見
  // 過程結束自動釋放
end;

procedure TForm1.btnDispClick(Sender: TObject);
var
  Disp: IDisplay;
begin
  Disp := TMyTool.Create;
  Disp.ShowMsg('僅顯示介面');
  Disp.ShowValue('答案', 42);
  // Disp 只能訪問 IDisplay 方法
end;

procedure TForm1.btnBothClick(Sender: TObject);
var
  Obj: TMyTool;
  Calc: ICalculator;
  Disp: IDisplay;
begin
  Obj := TMyTool.Create; // 持有對象引用 (RefCount=1)
  try
    Calc := Obj;         // 隱式 QueryInterface → ICalculator (RefCount=2)
    Disp := Obj;         // 隱式 QueryInterface → IDisplay   (RefCount=3)

    Calc.Add(10, 20);
    Disp.ShowValue('和', 30);

    // 關鍵:介面賦值之間可相互轉換 (QueryInterface)
    Disp := Calc;        // ICalculator → IDisplay (同一對象不同視圖)
    Disp.ShowMsg('從 Calc 轉來的 Disp');
  finally
    Obj.Free; // 手動釋放對象,介面變量離開作用域再 _Release 兩次
  end;
end;

end.

核心概念圖解(文字版)

TMyTool 實例 (RefCount=3)
   │
   ├─ 作為對象變量 Obj ─────────────────────→ 可調用所有 public 方法
   │
   ├─ QueryInterface(ICalculator) → Calc ───→ 只見 Add, Mul
   │
   └─ QueryInterface(IDisplay)    → Disp ───→ 只見 ShowMsg, ShowValue

關鍵:Calc := Disp 之間轉換,本質是同一對象內部 QueryInterface,
     返回同一對象的不同介面指針,RefCount 相應增減。

介面編程十大必知規則(新手速查)

#規則說明
1命名慣例介面以 I 開頭 (如 ICalculator),類以 T 開頭
2根介面所有介面隱式繼承 IInterface (即 IUnknown),可省略不寫
3成員限制只能有方法、屬性,**無字段**
4可見性介面成員**隱式 public**,無 private/protected/public 修飾符
5無虛擬修飾介面方法無 virtual/dynamic/abstract/override,純契約
6介面繼承介面可單繼承介面 (IChild = interface(IParent)),Win32 不支援多繼承
7類實現多介面TClass = class(TBase, IIntf1, IIntf2, ...) 逗號分隔
8介面視圖隔離介面變量**只見自身聲明的成員**,類多豐富介面只暴露契約
9基類選擇實現介面類**強烈建議繼承 TInterfacedObject**,自帶引用計數;繼承 TObject 需手動實現 _AddRef/_Release
10自動釋放介面變量離開作用域/賦 nil/重新賦值 → 自動 _Release → 歸零調用 Destroy;**對象變量無此機制**

關於 as 轉換的特殊說明


// 介面有 GUID 才能用 as
Disp := Calc as IDisplay;  // 安全轉換,失敗拋 EIntfCastError

// 無 GUID 介面只能用硬轉換 (不安全,失敗 AV)
Disp := IDisplay(Calc);    // 編譯器信任你,運行時無檢查
// 或
Disp := Calc as IDisplay;  // 編譯錯誤:介面無 GUID

建議:所有跨單元/跨模塊介面**統一加 GUID** (Ctrl+Shift+G),內部單元可省略但建議養成習慣。

總結

這個「第一個例子」其實展示了介面編程的三大核心能力:

  1. 多介面聚合:一個類通過 class(..., IIntf1, IIntf2) 實現多契約。
  2. 視圖隔離:ICalculator 變量只見計算方法,IDisplay 只見顯示方法——接口隔離原則 (ISP) 天然落地。
  3. QueryInterface 動態轉換:Calc := Disp 運行時查詢同一對象的不同能力視圖,無需知道具體類型。

掌握這三點,你就掌握了 Delphi 介面編程 80% 的實戰精髓。

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