經典案例:Person.Exchange(Person p, int amount) 直接存取 p._money(private 欄位)。編譯器允許,因為封裝邊界在類別層級,而非物件層級。這不僅是語言實作細節,更揭示了靜態型別系統的根本限制。
public class Person {
private int _money;
public void Exchange(Person p, int amount) {
p._money -= amount; // 編譯合法:同類別可存取 private
this._money += amount;
}
}
理想語意:this 可存取自己私有欄位,p(不同實例)不可。
現實:編譯器只能靜態檢查「是否同類別」,無法動態判斷「是否同實例」。
上例中,型別系統認為程式合法;但業務語意要求「未經允許不得動用他人錢包」,此約束超出型別系統表達能力。
public void Exchange(Person p, int amount) {
Contract.Requires(p != this); // 前置條件:不可自我交易
Contract.Requires(amount > 0);
Contract.Ensures(this._money == Contract.OldValue(this._money) + amount); // 後置條件
p.Change(-amount); // 透過公開方法,受 p 內部邏輯保護
this._money += amount;
}
Contract.Requires:前置條件(呼叫端責任)Contract.Ensures:後置條件(方法實作責任)Contract.Invariant:類別不變條件(如 _money >= 0)靜態檢查器可在編譯期驗證部分契約;執行期違約拋出 ContractException,比 Assert 更嚴謹。
// 模板方法模式:封裝流程骨架,委托具體步驟
public TData Get<TData>(
Func<bool, TData> tryGetCache, // 嘗試取快取
Func<TData> getFromSource, // 從資料源取得
Action<TData> setCache // 寫入快取
) { ... }
委托參數即「微型契約」:呼叫端必須提供符合簽名與語意的實作。高階函數以組合替代繼承,以委托替代介面,實現契約式設計的輕量化落地。
Java 無委托,用單方法介面(SAM)+ 匿名類模擬。缺點:語法冗長、無法捕獲非 final 區域變數。C# 匿名函數(Lambda)支援完整閉包,使契約式 API 設計極致簡潔——這是語言特性對設計模式落地的直接賦能。
類級封裝是妥協產物,非設計終點。契約式程式設計(Code Contracts、Spec#、現代 requires/ensures 模式)嘗試彌補靜態型別系統的語意表達缺口。高階函數則提供了另一條路徑:以委托組合表達契約,編譯器驗證簽名,測試驗證語意。**好的架構,是在語言能力邊界內,為業務不變量建立可驗證的防線。**