面向物件三大特性在 COM 的體現
1. 封裝性:徹底的二進制封裝
- COM 物件內部狀態完全不可直接存取,所有互動必須透過介面方法。
- 介面定義與實作分離,客戶端僅依賴介面合約(vtable 布局)。
- 實現細節(記憶體佈局、演算法、資料結構)對客戶端完全不透明。
2. 多型性:三層次的多型實現
- 介面成員函數多型:同一介面方法在不同物件有不同實作(虛擬函式機制)。
- 單一介面多型:不同物件實作同一介面,客戶端以介面指標統一操作。
- 介面群組多型:物件支援一組介面(Implemented Categories),客戶端依類別查詢支援的介面集合。
3. 重用性:二進制層級的重用
C++ 重用在原始碼層級(繼承);COM 重用在二進制碼層級,提供兩種標準機制:
包含 —— 客戶/伺服器嵌套
- 外部物件 B 包含內部物件 A,B 實作介面
ISomeInterface 並委派呼叫給 A。
- B 可擴展/修改 A 的行為(前置/後置處理、參數轉換、結果過濾)。
- B 的介面與 A 可不同;A 生命週期包含在 B 內。
- 本質:委派模式,重用建立在客戶/伺服器相對關係上。
聚合 —— 真正的二進制重用
- 內部物件 A 直接向外部客戶端暴露其介面,外部物件 B 不攔截呼叫。
- A 的生命週期仍由 B 控制(建構、銷毀、初始化)。
- 關鍵協作:A 的
QueryInterface 在被聚合時,對不支援的介面(含 IUnknown)必須委派給外部物件的 IUnknown(pUnkOuter)。
- 雙重
IUnknown:委派 IUnknown(對外)與非委派 IUnknown(內部實作)。
- 本質:介面合併,客戶端感覺不到內部物件存在,實現真正的二進制重用。
委派與非委派 IUnknown
// 物件建立函數參數
HRESULT CoCreateInstance(..., LPUNKNOWN pUnkOuter, ...);
// pUnkOuter == NULL:正常使用
// pUnkOuter != NULL:被聚合
// 內部物件實作兩套 IUnknown:
// 1. 非委派 IUnknown:正常 AddRef/Release/QueryInterface(內部用)
// 2. 委派 IUnknown:
// - 正常使用時 → 轉呼叫非委派 IUnknown
// - 被聚合時 → 轉呼叫 pUnkOuter(外部物件的 IUnknown)
C++ 不支援類別實作兩個同名介面,需定義新類別實作委派 IUnknown(COM 透過 vtable 識別介面,不靠類別名稱)。
介面呼叫的進程透明性
- 進程內元件:直接函數呼叫(同一位址空間)。
- 進程外元件:客戶端呼叫代理器,代理器透過 LPC/RPC 與伺服器端存根通訊,存根呼叫實際物件。
- 客戶端與物件雙方感覺不到進程邊界,介面呼叫語法完全一致。
總結
COM 以介面為核心實現徹底封裝、三層次多型,並提供包含(委派)與聚合(介面合併)兩種二進制重用機制,配合進程透明性,構建跨語言、跨進程、跨機器的元件互操作基礎。