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

 

例子:
孙悟空的猴毛:孙悟空拔下一嘬猴毛,轻轻一吹就会变出好多的孙悟空来。
红警中的箱子:在玩红警时会出现一些小箱子(魔法箱子),有的小箱子被一辆坦克撞开的时候会变出一辆同样的坦克。

在C#中实现 原型模式很简单,在ICloneable接口中有一个Clone的方法,只要实现该接口就可以通过重写Clone方法来实现对象的拷贝。
原型模式中的拷贝分为“浅拷贝”和“深拷贝”:
“浅拷贝”:对值类型的成员变量进行值的复制,对引用类型的成员变量只复制引用,不复制引用的对象。
“深拷贝”:对值类型的成员变量进行值的复制,对引用类型的成员变量也进行引用对象的复制。

 

    //抽象原型,实现了ICloneable接口,为“具体原型”类接供了实现规则
    abstract class ProtoType:ICloneable
    {
        private string s;
        public ProtoType(string str)
        {
            this.s = str;
        }
       // 显示成员变量的信息
        public void show()
        {
            Console.WriteLine(s);
        }
       //未实现ICloneable接口中的Clone方法,留给“具体原型”类来实现
        public abstract object Clone();
    }
    //具体原型,即要被复制的对象的类,它继承子抽象原型。
    class ConcreteProtoType : ProtoType
    {
        public ConcreteProtoType(string str)
            : base(str)
        { }
       //实现了“抽象原型”中未实现的Clone方法
        public override object Clone()
        {
            //调用Object类中的MemberwiseClone方法实现浅拷贝
           return (ProtoType)this.MemberwiseClone();
        }
    }
    //客户程序
    public class Client
    {
        public static void Main()
        {
            ConcreteProtoType p1 = new ConcreteProtoType("hello world");
             //实现对p1的拷贝
            ConcreteProtoType p2 = (ConcreteProtoType)p1.Clone();
            p1.show();
            p2.show();
        }
    }
   
深拷贝与浅拷贝:
    浅拷贝:只需在“具体原型”类的Clone方法中执行return this.MemberwiseClone();就可以实现对象的浅拷贝,如上例。
    深拷贝:需要在“具体原型”类的Clone方法中实例化一个新的“具体原型”对象,并把当前对象成员变量的值赋给新生成的对象成员变量
    //深拷贝
    class DeepCopy : ICloneable
    {
        private List<string> array = new List<string>();
        public DeepCopy(params string[] str)
        {
            foreach (string s in str)
            {
                array.Add(s);
            }
        }

       //改变成员变量,以验证是否真的实现了深拷贝。
       public void Add(string s)
        {
            array.Add(s);
        }
        public object Clone()
        {

            //新实例化一个DeepCopy对象,并在构造函数中对成员变量被始化。
           return new DeepCopy(array.ToArray());
        }
       //显示当前对象的成员变量的内容
       public void Show()
        {
            Console.WriteLine("======DeepCopy======");
            foreach (string s in array)
            {
                Console.WriteLine(s);
            }
        }
    }
    class Client
    {
        public static void Main(string[] args)
        {
            DeepCopy dc = new DeepCopy("hello", "world");

           //调用dc的Clone方法进行拷贝,此时的拷贝是深拷贝。
            DeepCopy dd = (DeepCopy)dc.Clone();
            dc.Add("Hello C#");
            dc.Show();
            dd.Show();
        }
    }

   
一个完整例子:
该例子演示了红警中“魔法箱子”中的原型模式的实现,引例子中注释的两行内容为浅拷贝的代码。
    //抽象原型类,是所有车辆的父类,实现了ICloneable接口。
    abstract class Vehicle : ICloneable
    {
        protected Hashtable info;
//存储车辆的信息(RGB颜色值和生命)
        public Vehicle(int red, int green, int blue, int blood)
        {
            info = new Hashtable();
            info.Add("red", red);
            info.Add("green", green);
            info.Add("blue", blue);
            info.Add("blood", blood);
        }
        //显示车辆成员变量中的信息
        public virtual void Show()
        {
            Console.WriteLine("======The Vehicle Property======");
            foreach (DictionaryEntry de in info)
            {
                Console.WriteLine(de.Key + ":" + de.Value);
            }
        }
       //当车辆受到攻击时,减少生命值的方法。
        public void Attacked(int bloodloose)
        {
            info["blood"] = (int)info["blood"] - bloodloose;
        }

       //有待于“抽象原型”实现的方法
        public abstract object Clone();
    }
    //“具体原型”长角坦克
    class HornTank : Vehicle
    {

        //坦克的类型名称
        private const string type = "horn tank";
        public HornTank(int red, int green, int blue, int blood)
            : base(red, green, blue, blood)
        { }
        public override void Show()
        {
            base.Show();
            Console.WriteLine(type);
        }
        public override object Clone()
        {
            //实现对长角坦克的深拷贝
            return new HornTank((int)info["red"], (int)info["green"], (int)info["blue"], (int)info["blood"]);
           //对长角坦克的浅拷贝
            //return this.MemberwiseClone();
        }
    }
    //“具体原型”轻坦克
    class LightTank : Vehicle
    {
        //坦克的类型名称
        private const string TYPE = "light tank";
        public LightTank(int red, int green, int blue, int blood)
            : base(red, green, blue, blood)
        { }
        public override void Show()
        {
            base.Show();
            Console.WriteLine(TYPE);
        }
        public override object Clone()
        {
            //实现对轻坦克的深拷贝
            return new LightTank((int)info["red"], (int)info["green"], (int)info["blue"], (int)info["blood"]);
            //实现对轻坦克的浅拷贝
            //return this.MemberwiseClone();
        }
    }
    //魔法箱子类,可以生成一辆同样的新坦克
    class MagicBox
    {
        public Vehicle Open(Vehicle v)
        {
            //根据v生成一个新的对象。
            return (Vehicle)v.Clone();
        }
    }
    class Client
    {
        public static void Main(string[] args)
        {
            //一辆轻坦克
           LightTank lt = new LightTank(100, 200, 150, 100);
            //该坦克被打掉46点生命值,剩下54点生命值。
            lt.Attacked(46);
            //一个魔法箱子
            MagicBox mb = new MagicBox();
            //魔法箱子打开时深拷贝一个同样的轻坦克,新的轻坦克也只有54点生命值。
            Vehicle ve = mb.Open(lt);
            //拷贝出的轻坦克 被打掉23点生命值,由于进行的是深拷贝,原轻坦克的生命值并未受影响。
            ve.Attacked(23);
            lt.Show();
            ve.Show();
        }
    }
    运行结果:
    

带Prototype Manager的原型模式


Prototype Manager用来管理要被创建的对象。实现对多个“具体原型”的统一管理。


有人问直接用new 运算符实例化一个类的对就得了,为什么非要这么麻烦地实现拷贝?
其实,直接使用new运算符和原型模式都可以创建出一个对象。而原型模式实际上是动态抽取当前对象运行时的状态,用当前对象的状态来生成新的对象实例。(车延禄)

在以下几种情况中应当采用原型模式来创建对象:
1、对象的构造函数非常复杂,在生成新对象的时候非常耗时间、耗资源的情况,使用原型模式可以简捷地创建新对象。
2、程序运行过程中,由于一些数据随进都在变化,事后很难使用构造函数再构造出一个和原来一样的对象。
3、不知道当前对象的状态,即目前不知道当前对象成员变量的值都变成了什么内容,故无法再用构造函数来构造和当前对象一样的对象了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值