將物件的成員建立為替身介面的成員,用來解耦物件之間的循環相依。
[Design Pattern] Substitute Interface
目的
將物件的成員建立為替身介面的成員,用來解耦物件之間的循環相依。
情景
假設開發人員接手一個系統,在系統裡有訂單物件、送貨物件,其中訂單物件使用送貨物件所提供的送貨方法。
當系統繼續設計下去,會發現送貨方法需要訂單物件的相關資訊才能決定如何送貨。例如說:送貨方法需要商品數量,來決定要派卡車送貨還是要派機車送貨;送貨方法需要商品價格來決定要用盒子包還是要用紙袋包。當送貨方法的邏輯越來越複雜的時候,最終就會發現送貨方法需要訂單物件的所有成員才能滿足送貨方法的運算需求。
送貨方法需要訂單物件的所有成員,很直覺的設計就是將訂單物件作為送貨方法的參數,用以提供訂單物件的所有成員來滿足送貨方法的運算需求。系統設計到了這個階段,就會發現訂單物件使用送貨物件、而送貨物件也使用了訂單物件,這也就是產生了物件之間循環相依的問題。(在.NET中,物件之間的循環相依是能夠通過編譯、並且執行的設計)
為了解決物件之間循環相依的問題,回過頭思考訂單物件、送貨物件之間的關係。會發現在送貨物件的送貨方法中,其實需要的是訂單物件的「所有成員」,而不是真的需要一個訂單物件。
這時開發人員可以將訂單物件的所有成員,建立為一個「替身介面」並且讓訂單物件來繼承這個介面,用來透過替身介面來提供訂單物件的所有成員。接著送貨物件中的送貨方法,改為使用這個替身介面做為參數,這樣就可以取得訂單物件的所有成員,而不需要將訂單物件作為送貨方法的參數。也就是說經由這樣的設計,讓訂單物件、送貨物件都改為相依於替身介面,進而就解除了訂單物件、送貨物件之間循環相依的問題。
結構
參與者
Substitute
- 提供Primary物件的所有成員。
Primary
- 實作Substitute介面。
- 持有Secondary物件的參考。
- 將本身視為Substitute介面來做為Secondary物件的函式參數,用以提供所有成員。
Secondary
- 使用Substitute介面做為函式參數,來取得Primary物件的所有成員。
合作方式
實作
-
類別圖
-
IOrder
public interface IOrder { // Properties int Quantity { get; set; } int Amount { get; set; } // Methods void Deliver(); }
-
Order
public class Order : IOrder { // Fields private readonly Deliverer _deliverer = new Deliverer(); // Properties public int Quantity{get; set;} public int Amount { get; set; } // Methods public void Deliver() { _deliverer.Deliver(this); } }
-
Deliverer
public class Deliverer { // Methods public void Deliver(IOrder order) { // Print Console.WriteLine(order.Quantity); Console.WriteLine(order.Amount); } }
-
Program
class Program { static void Main(string[] args) { // Test var order = new Order(); order.Quantity = 12345; order.Amount = 67890; order.Deliver(); // End Console.WriteLine("End..."); Console.ReadLine(); } }
-
執行結果
下載
範例程式碼:點此下載
能以更簡潔的文字與程式碼,傳達出程式設計背後的精神。
真正做到「以形寫神」的境界。