# 5. Prototype ###### tags: `DesignPatterns` ## 關於 Prototype 本篇將討論以下幾個問題 > ### 1. 關於 Prototype > ### 2. Shallow copy(淺複製) & Deep copy(深複製) > ### 3. UML > ### 4. 將 UML 轉為程式碼 > ### 5. 情境 --- ## 測試環境: >OS:Windows 10 >IDE:Visual Studio 2019 --- ## 1. 關於 Prototype > Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype. > > by [Gang of Four](https://en.wikipedia.org/wiki/Design_Patterns) - 依據原型(的 abstract class/interface)創建出原型實體,並通過複製此原型來創建新的物件 Prototype(原型)屬於創建型(Creational Patterns),當遇到**內部差異小但創建成本較高**(e.g. 呼叫外部資料耗時)的實體時,透過 Prototype 以複製方式來創建新的實體,跳過初始化的過程來減少資源的浪費。 優點: - 減少反覆實體初始化的過程 缺點: - 被複製的實體若繼承關係較複雜,則可能會大幅增加整體複雜度 --- ## 2. Shallow copy(淺複製) & Deep copy(深複製) Shallow copy(淺複製) - 只複製記憶體位置 - 被複製物件修改可能會將複製物件一同修改 Deep copy(深複製) - 建立新記憶體位置,並放置一份相同的資料 - 被複製物件與複製物件各自獨立 --- ## 3. UML  Class 間關聯: - Client 關聯 Prototype - ConcretePrototype 繼承 Prototype Class: - Client:呼叫端 - Prototype:原型的抽象類別或介面 - ConcretePrototype:原型的實作,在類別中加上複製邏輯 --- ## 4. 將 UML 轉為程式碼 這邊所使用的複製方法 MemberwiseClone() 請參考 [MSDN](https://docs.microsoft.com/zh-tw/dotnet/api/system.object.memberwiseclone?view=net-5.0) 原型的介面 ```C# /// <summary> /// 原型的介面 /// </summary> public interface IPrototype { string Id { get; } public IPrototype Clone(); } ``` 原型實作 ```C# /// <summary> /// 原型實作 /// </summary> public class ConcretePrototype : IPrototype { public string Id { get; } public ConcretePrototype(string id) { Id = id; } // Returns a shallow copy public IPrototype Clone() { return (IPrototype)this.MemberwiseClone(); } } ``` 1. 建立類別 2. 呼叫`Clone()`取得複製類別 ```C# static void Main(string[] args) { Default.ConcretePrototype prototype = new Default.ConcretePrototype("Test"); Default.ConcretePrototype clone = (Default.ConcretePrototype)prototype.Clone(); Console.WriteLine($"Cloned: {clone.Id}"); Console.ReadLine(); } ``` 執行結果 ```Console Cloned: Test ``` --- ## 5. 情境 我們接到了一個數間門市每天開店時要取得倉庫庫存量資訊的需求 - 不同分店會有各自資訊要記錄 - 倉庫庫存資訊資料量很大,API 傳輸很慢 分店資訊介面 ```C# /// <summary> /// 分店資訊介面 /// </summary> public interface IStore { string StoreId { get; set; } string InStockData { get; set; } public IStore Clone(string storeId); } ``` 分店資訊實作 ```C# /// <summary> /// 分店資訊實作 /// </summary> public class Store : IStore { public string StoreId { get; set; } public string InStockData { get; set; } public Store(string storeId) { StoreId = storeId; } public void GetInStockData() { InStockData = "庫存量"; Console.WriteLine($"取得 {InStockData}"); } // Returns a shallow copy public IStore Clone(string storeId) { var clone = (IStore) this.MemberwiseClone(); clone.StoreId = storeId; return clone; } } ``` 1. 先建立 Taipei 店物件 2. 取得庫存資訊 3. 呼叫`Clone()` 4. 由 Clone 取得的 Tainan 店不需要再次呼叫取得庫存資訊 ```C# static void Main(string[] args) { Situation.Store store = new Situation.Store("Taipei"); store.GetInStockData(); Situation.Store clone = (Situation.Store)store.Clone("Tainan"); Console.WriteLine($"Store StoreId: {store.StoreId}"); Console.WriteLine($"Store InStockData: {store.InStockData}"); Console.WriteLine($"Clone StoreId: {clone.StoreId}"); Console.WriteLine($"Clone InStockData: {clone.InStockData}"); Console.ReadLine(); } ``` 執行結果 ```Console 取得 庫存量 Store StoreId: Taipei Store InStockData: 庫存量 Clone StoreId: Tainan Clone InStockData: 庫存量 ``` --- ## 完整程式碼 GitHub:[Creational_04_Prototype](https://github.com/darionnnnnn/blog/tree/master/Blog/Creational_04_Prototype) --- ## 總結 ### 範例中只對於 Prototype 的重點實作,實際上 Clone() 內要使用 Shallow copy(淺複製)、Deep copy(深複製)或是自訂的複製方法都可以,且除了範例中複製大量資訊避免還要花時間重新載入之外,對於只有建構時需要消耗大量資源的運算、I/O等,都可以藉由 Prototype 來減少資源的消耗。 --- ## 參考資料 1. [Design Patterns](https://en.wikipedia.org/wiki/Design_Patterns) 2. [大話設計模式](https://www.tenlong.com.tw/products/9789866761799) 2. [dofactory](https://www.dofactory.com/) 3. [Refactoring.Guru](https://refactoring.guru/) --- ## 新手上路,若有錯誤還請告知,謝謝
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up