欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

设计模式-原型模式详解

程序员文章站 2022-12-21 11:29:43
一、原型模式的概念 原型模式属于创建型设计模式。当要创建的对象类型由原型实例确定时使用它,该实例被克隆以生成新对象。 此模式用于 1、避免客户端应用程序中的对象创建者的子类,如工厂方法模式。 2、避免以标准方式创建新对象的固有成本(例如,使用'new'关键字),当它对于给定的应用程序来说过于昂贵时。 ......

一、原型模式的概念

  原型模式属于创建型设计模式。当要创建的对象类型由原型实例确定时使用它,该实例被克隆以生成新对象。

  此模式用于

  1、避免客户端应用程序中的对象创建者的子类,如工厂方法模式。

  2、避免以标准方式创建新对象的固有成本(例如,使用'new'关键字),当它对于给定的应用程序来说过于昂贵时。

  上述定义来自wiki,简单来说,就是创建一个全新的对象代价太高时,比如耗时太长时,能直接从现有实例中以较小的代价克隆出一个对象。

二、原型模式的实现

  实现原型模式的方式之一为克隆,下面我们来实现一下

  1、先定义一个抽象原型,包含两个克隆的虚方法(方便子类重写)

namespace prototypepattern
{
    using system;
    using system.io;
    using system.runtime.serialization.formatters.binary;

    [serializable]
    public abstract class abstractprototype 
    {
        /// <summary>
        /// 浅克隆
        /// <remarks>使用objectmemberwiseclone</remarks>
        /// </summary>
        /// <returns>当前对象副本</returns>
        public virtual abstractprototype clone()
        {
            return (abstractprototype)this.memberwiseclone();
        }

        /// <summary>
        /// 深克隆
        /// <remarks>使用二进制序列化反序列化实现</remarks>
        /// </summary>
        /// <returns>当前对象副本</returns>
        public virtual abstractprototype deepclone()
        {
            memorystream stream = new memorystream();
            binaryformatter binaryformatter = new binaryformatter();
            binaryformatter.serialize(stream, this);
            stream.position = 0;
            var instance = (abstractprototype)binaryformatter.deserialize(stream);
            stream.disposeasync();
            return instance;
        }
    }
}

2、需要实现原型模式的类继承此抽象类即可

namespace prototypepattern
{
    using system;

    [serializable]
    public class concreteprototype : abstractprototype
    {
        public int number { get; set; }

        public person person { get; set; }
    }
}

person类

namespace prototypepattern
{
    using system;

    [serializable]
    public class person
    {
        public string name { get; set; }

        public int age { get; set; }
    }
}

以上代码就实现了原型模式。

其中的克隆方式又分为浅克隆,深克隆,深克隆使用的是序列化反序列化的方式(需要在类上增加[serializable]特性)。

浅克隆
复制出来的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。

深克隆
复制出来的所有变量都含有与原来的对象相同的值,那些引用其他对象的变量将指向复制出来的新对象,是一个全新的副本。

我们来测试一波

            {
                concreteprototype no0 = new concreteprototype() { number = 0, person = new person() { age = 17, name = "vincent" } };
                console.writeline("第一次构造");
                console.writeline($"no0:number:{no0.number},age:{no0.person.age},name:{no0.person.name}");

                console.writeline("从no0浅克隆到no1");
                concreteprototype no1 = (concreteprototype)no0.clone();

                console.writeline("修改no1");
                no1.person.age = 18;
                no1.person.name = "vincent1";
                console.writeline($"no0:number:{no0.number},age:{no0.person.age},name:{no0.person.name}");
                console.writeline($"no1:number:{no1.number},age:{no1.person.age},name:{no1.person.name}");
                console.writeline("******************");

                console.writeline("从no0浅克隆到no2");
                concreteprototype no2 = (concreteprototype)no0.deepclone();
                console.writeline("修改no2");
                no2.person.age = 19;
                no2.person.name = "vincent2";
                console.writeline($"no0:number:{no0.number},age:{no0.person.age},name:{no0.person.name}");
                console.writeline($"no2:number:{no2.number},age:{no2.person.age},name:{no2.person.name}");
                console.writeline("******************");
            }

看看结果

设计模式-原型模式详解

可以看到,通过浅克隆得到的对象和原对象共享引用对象,深克隆的反之。

以上是一般的实现方式,但是这里使用的是继承抽象原型类的方式。我们知道,c#里是单继承,我们这里继承了抽象的原型类,就不能继承其他类了,难免会有些不方便。下面我们来改进一下。

三、改进

  我们知道.net框架里为我们提供了icloneable接口,此接口只包含一个返回值为object的clone方法。object类中提供了返回值为object、访问级别为protected的memberwiseclone方法。那么我们只需要继承icloneable接口,实现clone方法,把clone方法访问级别改为public即可实现原型模式,如下

namespace prototypepattern
{
    using system;

    [serializable]
    public class concreteprototype2 : icloneable
    {
        public int number { get; set; }

        public person person { get; set; }

        /// <summary>creates a new object that is a copy of the current instance.</summary>
        /// <returns>a new object that is a copy of this instance.</returns>
        public object clone()
        {
           return this.memberwiseclone();
        }
    }
}

这里只有浅克隆,没有深克隆,怎么办?我们使用扩展方法,为icloneable接口扩展一个深克隆方法,如下

namespace prototypepattern
{
    using system;
    using system.io;
    using system.runtime.serialization.formatters.binary;

    public static class icloneableextension
    {
        public static t deepclone<t>(this icloneable instance)
        {
            memorystream stream = new memorystream();
            binaryformatter binaryformatter = new binaryformatter();
            binaryformatter.serialize(stream, instance);
            stream.position = 0;
            var duplicate = binaryformatter.deserialize(stream);
            stream.disposeasync();
            return (t)duplicate;
        }
    }
}

 四、总结

  以上的实现只能应对一般的情况,如果需要应对复杂的情况,如克隆对象中指定的对象(例如单据的复制),就需要改进或增加克隆的实现方式。

  文中的代码下载:https://github.com/hzhhhbb/prototypepattern

五、参考资料

  https://en.wikipedia.org/wiki/prototype_pattern