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


    场景分析:

    前面我们提到,交易对象Trade,还有继承他的债券交易BondTrade、期货交易FutureTrade。

    现在有一个需求,需要提供方法将交易拆分成多笔小交易。

    代码如下(如果没有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方法没有做到通用处理。

    上面的问题其实就是:

    SplitTrade方法本身,已经有了参数Trade(可能为BondTrade实例,或者FutureTrade实例,或者将来的**Trade实例 ),一个接口类型的交易对象实例,但是我们只知道这个参数是交易类型,并不知道是哪种交易,现在要新创建新的交易子类对象(如果前面是BondTrade实例,我还需要创建该实例,属性值同原来的BondTrade实例),相当于通过接口来创建对象。

    原型模式就是解决这种问题的。

    原型模式的本质:

    用原型实例来指定创建对象的种类,并通过拷贝这些原型创建新的对象。

    这句话分为两点:

    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),减少了使用方对这些具体实现的依赖。

  • 相关阅读:
    android Edittext自定义输入字符和类型
    让android webView使用系统默认浏览器内核直接解析,不弹出选择浏览器选项
    java对象中继承和变量初始化顺序浅析
    android判断pad还是手机
    我不知道自己想要什么
    计算机网络概述
    2020/2/27-28
    操作系统概述
    数据模型
    数据库系统概述
  • 原文地址:https://www.cnblogs.com/liaozh/p/3413311.html
Copyright © 2020-2023  润新知