设计模式-原型模式(Prototype)

场景分析: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),减小了使用方对这些具体实现的依赖。

相关文章
相关标签/搜索