當兩個介面 IA、IB 僅共享同一個實作類別 AB 時,在方法參數為 IA 卻嘗試轉型為 IB 呼叫成員,這樣的設計是否合理?本文透過代碼範例剖析此模式的優缺點與適用場景。
interface IA { }
interface IB { void fun(); }
class AB : IA, IB {
public void fun() {
Console.WriteLine("AB.fun() called.");
otherOperation();
}
public void otherOperation() { Console.WriteLine("Commercial Operation."); }
}
class Test {
public static void Main() {
Test t = new Test();
t.testfun(new AB()); // 實際傳入 AB
}
public void testfun(IA a) { // 編譯時類型 IA
IB b = a as IB; // 跨介面轉型
if (b != null) b.fun();
Console.Read();
}
}
IB b = a as IB; 這行代碼在編譯期合法,執行期若 a 實際為 AB 則成功,否則 b 為 null。這屬於「執行期型別發現」模式,繞過編譯期靜態檢查。
IA,不需知道 IB 存在,降低介面依賴。IA 但不實作 IB 的類別時,呼叫端代碼無需修改,僅在轉型失敗時靜默跳過。IA,擴展功能由 IB 提供。IB),違反介面隔離原則 (ISP)。IA 與 IB 無繼承關係,強行轉型暗示兩者有隱含關聯,增加認知負擔。此模式常見於 Visitor Pattern 與 Double Dispatch 變體中。參考文獻中 GraphVisitor nv = v as GraphVisitor; 同屬此類:ElementVisitor、GraphVisitor 僅共享 XMLVisitor 子類別。
IB 功能屬核心且穩定,考慮合併至 IA 或讓 IA 繼承 IB。IB 封裝為策略物件,透過屬性注入,編譯期可見。ISupportOptionalFeature 標記介面,實作類別明確宣告支援,呼叫端檢查 a is ISupportOptionalFeature 後再轉型。跨介面轉型屬於「技巧」而非「最佳實踐」。在插件系統、可選擴展點等受控場景可接受,但核心業務邏輯應避免,優先採用明確的介面繼承或組合設計,確保靜態型別安全與代碼可讀性。