場景分析:spa
前面咱們提到,交易對象Trade,還有繼承他的債券交易BondTrade、期貨交易FutureTrade。code
如今有一個需求,須要提供方法將交易拆分紅多筆小交易。對象
代碼以下(若是沒有clone方法):繼承
/// <summary> /// 拆分交易 /// </summary> /// <param name="aTrade">原始交易</param> /// <param name="aSplitCount">拆分的筆數</param> public static List<Trade> SplitTrade(Trade aTrade, int aSplitCount) { List<Trade> tmpTrades = new List<Trade>(); if (aTrade == null) { return null; } if (aSplitCount == 0) { tmpTrades.Add(aTrade); return tmpTrades; } for (int i = 0; i < aSplitCount; i++) { //注意:此時不能直接賦值,否則新建立的Trade對象跟原始Trade對象指向同一個實例 //Trade trade = aTrade; if (aTrade is BondTrade) { //若是交易是債券交易,建立新的債券對象實例 BondTrade tmpTrade = new BondTrade(); //依次賦值屬性,須要賦值Trade自己的屬性和BondTrade新增的屬性 tmpTrade.NO = aTrade.NO; tmpTrade.ORDCOUNT = aTrade.ORDCOUNT; tmpTrade.BondAmount = (aTrade as BondTrade).BondAmount; //每筆明細交易的ORDCOUNT必須平分 tmpTrade.ORDCOUNT = aTrade.ORDCOUNT / aSplitCount; tmpTrades.Add(tmpTrade); } else if (aTrade is FutureTrade) { //若是交易是期貨交易,建立新的期貨對象實例 FutureTrade tmpTrade = new FutureTrade(); //依次賦值屬性,須要賦值Trade自己的屬性和FutureTrade新增的屬性 tmpTrade.NO = aTrade.NO; tmpTrade.ORDCOUNT = aTrade.ORDCOUNT; tmpTrade.FutureAmount = (aTrade as FutureTrade).FutureAmount; //每筆明細交易的ORDCOUNT必須平分 tmpTrade.ORDCOUNT = aTrade.ORDCOUNT / aSplitCount; tmpTrades.Add(tmpTrade); } } return tmpTrades; }
很容易看出上面代碼的弊端:遞歸
1.SplitTrade方法自己應該是一個通用的拆分交易的方法,操做的是基類Trade,內部是不該該知道具體交易的。上面的代碼中拆分交易的方法依賴了Trade的具體實現對象。接口
2.不利於擴展。若是新增一個交易類型,如回購交易,須要修改上面的代碼。SplitTrade方法沒有作到通用處理。原型
上面的問題其實就是:it
SplitTrade方法自己,已經有了參數Trade(可能爲BondTrade實例,或者FutureTrade實例,或者未來的**Trade實例 ),一個接口類型的交易對象實例,可是咱們只知道這個參數是交易類型,並不知道是哪一種交易,如今要新建立新的交易子類對象(若是前面是BondTrade實例,我還須要建立該實例,屬性值同原來的BondTrade實例),至關於經過接口來建立對象。class
原型模式就是解決這種問題的。擴展
原型模式的本質:
用原型實例來指定建立對象的種類,並經過拷貝這些原型建立新的對象。
這句話分爲兩點:
1.建立新的對象實例
2.爲新的對象實例複製原型實例的屬性值
原型模式經過一個原型實例來建立新的對象,再也不關係這個實例自己的類型,也不關心他的具體實現,只要他自身實現了拷貝本身的方法便可(Clone)。經過該方法,就能夠直接返回自己對象實例,不用在外面經過New去實現。
原型模式的組成:
ProtoType:定義一個Clone接口,使得繼承他的子類都必須實現本身的Clone方法。即上文中的Trade。
ConcreteProtoTYpe:實現ProtoType類的Clone接口,經過Clone方法,能夠新增一個對象,並把原始對象的屬性賦值給新建立對象。即上文中的BondTrade、FutureTrade。
Client:使用原型方法的地方。經過一個原型實例,克隆自身來建立新的對象實例。即上文中的SplitTrade方法。
代碼改進:
根據原型模式,咱們對上面的代碼進行改進,在Trade類中新增Clone方法(拷貝NO、ORDCOUNT等屬性)。而後在BondTrade、FutureTrade方法重寫Clone方法(拷貝BondAmount、FutureAmount等自身子類的屬性)。
幸運的是,C#自身就有IClone接口和MemberwiseClone方法。在上面的例子中,只用在Trade基類中繼承IClone接口,而且實現方法,方面裏面簡單調用MemberwiseClone便可。
修改後的SplitTrade方法變爲:
/// <summary> /// 拆分交易(使用原型模式) /// </summary> /// <param name="aTrade">原始交易</param> /// <param name="aSplitCount">拆分的筆數</param> public static List<Trade> SplitTradeWithProtoType(Trade aTrade, int aSplitCount) { List<Trade> tmpTrades = new List<Trade>(); if (aTrade == null) { return null; } if (aSplitCount == 0) { tmpTrades.Add(aTrade); return tmpTrades; } for (int i = 0; i < aSplitCount; i++) { //直接調用Clone方法便可 Trade trade = aTrade.Clone() as Trade; trade.ORDCOUNT = aTrade.ORDCOUNT / aSplitCount; tmpTrades.Add(trade); } return tmpTrades; }
補充描述:
1.Clone方法自己至關於New了一個對象。不一樣的是New一個對象實例,通常屬性是沒有值或者只有默認值。Clone出來的實例,一般屬性是有值的,屬性的值就要原型對象的屬性值。
2.原型對象和克隆出來的對象。雖說是拷貝出來的,可是指向是不一樣的,本質上仍是不一樣的對象。
3.深克隆和淺克隆。
淺克隆,拷貝對象的全部值類型屬性。
深克隆,除拷貝對象的全部值類型屬性之外,還拷貝對象的全部引用類型,只是引用的
深克隆有個特色,就是若是屬性的值是引用類型,會一直遞歸拷貝下去,知道拷貝到值類型爲止。所以,要想深克隆拷貝成功,克隆過程當中涉及的全部對象都要實現Clone方法,不然將會致使拷貝失敗。
原型模式其實就是一個Clone方法,本質是克隆生成對象。
原型模式的好處是在調用時(如上面的SplitTrade方法),只知道接口類型(Trade),不知道具體的實現類型(BondTrade、FutureTrade),減小了使用方對這些具體實現的依賴。