天天看点

(31)C#设计模式——原型模式(Prototype Parrern)

引言

在软件系统中,当创建一个类的实例的过程很昂贵或很复杂,并且我们需要创建多个这样类的实例时,如果我们使用new操作符去创建这样的类实例,这未免会增加创建类的复杂度和耗费更多的内存空间,因为这样在内存中分配多个一样的类实例对象。如果采用工厂模式来封装类创建这样的系统的话,随着产品类的不断增加,导致子类的数量不断增多,反而增加了系统复杂程度,所以使用工程模式来封装类创建过程并不合适,可以使用原型模式来解决这个问题。因为每个类实例是相同的, 当我们需要多个相同的类实例时,没必要每次都用new操作符去创建相同的类实例,此时我们的思路是:只创建一个类实例对象,如果后面需要多个这样的实例,可以通过对原来对象拷贝一份来完成创建,这样内存中就不需要创建多个相同的类实例,从而减少内存的消耗和达到类实例的复用。这个思路正是原型模式的实现方式。

具体实现

下面以孙悟空拔猴毛变成孙悟空的例子来说明原型模式的实现。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace _21PrototypePatternDemo
{
    /// <summary>
    /// 测试类
    /// </summary>
    class Program
    {
        static void Main(string[] args)
        {
            //原型
            MonkeyKingPrototype prototypeMonkeyKing = new ConcreteProtoType("MonkeyKing");
            //变一个
            MonkeyKingPrototype clone1 = prototypeMonkeyKing.Clone() as ConcreteProtoType;
            Console.WriteLine("Cloned:\t"+clone1.Id);
            //变两个
            MonkeyKingPrototype clone2 = prototypeMonkeyKing.Clone() as ConcreteProtoType;
            Console.WriteLine("Cloned:\t" + clone2.Id);
            Console.ReadLine();
        }
    }
    /// <summary>
    /// 孙大圣原型
    /// </summary>
    public abstract class MonkeyKingPrototype
    {
        public string Id { get; set; }
        public MonkeyKingPrototype(string id)
        {
            this.Id = id;
        }
        //克隆方法
        public abstract MonkeyKingPrototype Clone();
    }
    /// <summary>
    /// 创建具体原型
    /// </summary>
    public class ConcreteProtoType : MonkeyKingPrototype
    {
        public ConcreteProtoType(string id):base(id)
        {

        }
        /// <summary>
        /// 浅拷贝
        /// </summary>
        /// <returns></returns>
        public override MonkeyKingPrototype Clone()
        {
            //调用MemberwiseClone()方法实现的是浅拷贝,另外还有深拷贝
            return (MonkeyKingPrototype)this.MemberwiseClone();
        }
    }
}
/*output:
Cloned: MonkeyKing
Cloned: MonkeyKing
*/
           

从上面的运行结果可以看出,创建的两个拷贝对象的ID属性与原型对象ID是一样的。

浅拷贝和深拷贝

浅拷贝是指当对象的字段值被拷贝时,字段引用的对象不会别拷贝。例如:如果一个对象有一个指向字符串的字段,并且我们对该对象做了一次浅拷贝,那么这两个对象将引用同一个字符串,而深拷贝是对对象实例中字段的引用的也进行拷贝,如果一个对象由一个指向字符串的字段,并且我们对该对象进行了深拷贝的话,那么我们将创建一个对象和一个新的字符串,新的对象引用新的字符串。也就是说,执行深拷贝创建的新对象和原来的对象不会共享任何东西,改变一个对象不会对另外一个对象造成任何影响,而执行浅拷贝创建的新对象与来对象共享成员,改变一个对象,另外一个对象的成员也会跟着改变。

类图:

(31)C#设计模式——原型模式(Prototype Parrern)

优缺点:

优点:

  1. 向客户端隐藏了创建新势力的复杂性
  2. 允许动态增加或减少产品类
  3. 简化了实例的创建结构,工厂方法模式需要有一个与产品类等级结构相同的等级结构,而原型模式不需要
  4. 产品类不需要事先确定产品的等级结构,因为原型模式使用于任何等级结构

缺点:

  1. 每个类都必须有一个克隆方法
  2. 配备克隆方法需要对类的功能进行全盘考虑,这对于全新的类不难,但对于已有的类不一定容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。

总结

原型模式用一个原型对象来指明所要创建的对象类型,然后用复制这个原型对象的方法来创建出更多的同类型对象,它与工厂方法模式的实现非常相似,其中原型模式中的Clone方法就类似工厂方法模式中的工厂方法,只是工厂方法模式的工厂方法是通过new操作符重新创建一个新的对象(相当于原型模式的深拷贝实现),而原型模式是通过调用MemberwiseClone方法来对原对象进行浅拷贝,也就是复制。