设计模式 5/23 原型模式

Posted Lionel Andrés Messi

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式 5/23 原型模式相关的知识,希望对你有一定的参考价值。

原型模式,一个深入浅出,检验你对基础知识了解的是否透彻的一个设计模式。

之所以这样定义,因为我栽了个跟头

想要吃透原型模式,就得深入理解 浅拷贝,深拷贝

想要深入吃透 浅拷贝,深拷贝

我们就要对 值类型 和 引用类型 有较深的认识

如果对 值类型 和 引用类型 有了较深的认识,在GC也至少有一定的修为了

..........我还能继续这样写下去很多,所以有开头的那句话,原型模式,一个深入浅出,检验你对基础知识了解的是否透彻的一个设计模式。

 

对值类型 和 引用类型 的浅薄的 入门,可以参考 几年前写的 面试前的准备---C#知识点回顾----02

里面有一些基础知识的介绍,可以作为了解 浅拷贝 和 深拷贝的铺垫

 

重点来了,看黑板!!!

深拷贝:指的是拷贝一个对象时,不仅仅把对象的引用进行复制,还把该对象引用的值也一起拷贝。这样进行深拷贝后的拷贝对象就和源对象互相独立,其中任何一个对象的改动都不会对另外一个对象造成影响。举个例子,你有一张光盘,你进行了多次刻录,产生了第二张,第三者光盘,那这三种光盘是相互独立的,第一张损坏或第二张损坏,都不会影响到第三张【光盘,会不会暴露年龄啊】。在.NET领域,值对象就是典型的例子,如int, Double以及结构体和枚举等。具体例子如下所示:

             int source = 11;
            // 值类型赋值内部执行深拷贝
            int copy = source;
            // 对拷贝对象进行赋值不会改变源对象的值
            copy = 22;//此时source仍然为11
            // 同样对源对象赋值也不会改变拷贝对象的值
            source = 33;//此时copy 仍然为22            
View Code

 怎么实现深拷贝,可以最原始的一个一个赋值,但也可以用反射或反序列化方式来实现,因人而异,具体根据具体需求定,寻找代码最合适的方法

 

浅拷贝:指的是拷贝一个对象时,仅仅拷贝对象的引用进行拷贝,但是拷贝对象和源对象还是引用同一份实体。此时,其中一个对象的改变都会影响到另一个对象。其实我们自己经常浅拷贝,在家,你是个宝宝的身份,在学校,你是学生的身份,在公司面前,你是职员,但如果你生病了,那么你所有身份都会生病,全都反应在你这个实体身上。在.NET中引用类型就是一个例子。如类类型。具体例子如下所示:

 Person you = new Person() { Info = "You" };
            Person schoolP = you; // 浅拷贝
            schoolP.Info = "学生"; // 拷贝对象改变Info值
            Person familyP = you;
            familyP.Info = "宝宝";
            Person emp = you;
            emp.Info = "职员";
            // 突然你生病了
            you.Info = "生病了";
            Console.WriteLine("schoolP.Info: {0};familyP.Info: {1};emp.Info:{2}", schoolP.Info, familyP.Info, emp.Info);
            Console.Read();


public class Person
{
    public string Info { get; set; }
}
View Code

 

重点!!!在C#中,浅拷贝的实现方式很简单,.NET自身也提供了实现,只需要实现接口 : ICloneable

.NET中值类型默认是深拷贝的,而对于引用类型,默认实现的是浅拷贝。所以对于类中引用类型的属性改变时,其另一个对象也会发生改变。当某个类的实例有个字段是值类型,那么实际该字段会和类的实例保持在同一个地方,即堆上

此次载的跟头就是在,让我痛定思痛的记住了,类都是引用类型

 

有了前面的铺垫,我们来看看原型模式

原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象

重点再次来临,2点

1.前半句告诉你原型还得自己建

2.后半句指点你,剩下的对象可以通过拷贝实现。具体是深拷贝还是浅拷贝,根据你的需求来定

举个例子,来品味下原型模式

在 War3 ,魔兽争霸[再次暴露年龄], 大海龟地图中有4个点位有野怪,这些野怪组成由 1个大海龟,2个小海龟组成,都是海龟,他们的差别是伤害输出的不同。

我们来看看怎么画这个图

先定义野怪,因为在地图中,除了海归,还有其他野怪

    /// <summary>
    /// 敌人,野怪
    /// </summary>
    [Serializable]
    public abstract class Enemy : ICloneable
    {
        protected Enemy(Location location, int power, int speed)
        {
            Location = location;
            Power = power;
            Speed = speed;
        }

        /// <summary>
        /// 出生点位
        /// </summary>
        public Location Location { get; set; }

        /// <summary>
        /// 攻击力[1~10 越来越强]
        /// </summary>
        public int Power { get; set; }

        /// <summary>
        /// 行动速度[1~10 越来越快]
        /// </summary>
        public int Speed { get; set; }

        /// <summary>
        /// 深拷贝
        /// </summary>
        /// <returns></returns>
        public abstract Enemy DeepClone();

        public abstract void Show();

        /// <summary>
        /// 浅拷贝
        /// </summary>
        /// <returns></returns>
        public object Clone()
        {
            return this.MemberwiseClone();
        }
    }
View Code

这是出生点位类

    /// <summary>
    /// 出生点位
    /// </summary>
    [Serializable]
    public class Location
    {
        /// <summary>
        /// 横坐标
        /// </summary>
        public int X;

        /// <summary>
        /// 纵坐标
        /// </summary>
        public int Y;

        public Location(int x, int y)
        {
            X = x;
            Y = y;
        }
    }
View Code

再定义海归野怪

    /// <summary>
    /// 海龟
    /// </summary>
    [Serializable]
    public class SeaTurtle : Enemy
    {
        public SeaTurtle(Location location, int power, int speed)
            : base(location, power, speed)
        {
        }

        public override Enemy DeepClone()
        {
            MemoryStream memoryStream = new MemoryStream();
            BinaryFormatter formatter = new BinaryFormatter();
            // 序列化成流
            formatter.Serialize(memoryStream, this);
            memoryStream.Position = 0;
            // 反序列化成对象
            SeaTurtle seaTurtle = (SeaTurtle)formatter.Deserialize(memoryStream);
            return seaTurtle;
        }

        public override void Show()
        {
            Console.WriteLine("我是海龟,攻击力:{0},速度:{1},出生点位:{2},{3}", Power, Speed, Location.X, Location.Y);
        }
    }
View Code

比如我们现在要生成一组野怪,我们看看怎么用原型模式实现

static void Main()
        {
            Location seaTurtleLocation = new Location(2, 2);

            //大海龟出生
            SeaTurtle bigSeaTurtle = new SeaTurtle(seaTurtleLocation, 10, 10);

            //照着大海龟 拷贝一个小海龟  A 
            Enemy smallSeaTurtleA = (SeaTurtle)bigSeaTurtle.DeepClone();
            smallSeaTurtleA.Location.X = seaTurtleLocation.X + 1;
            smallSeaTurtleA.Power = bigSeaTurtle.Power / 2;

            //再直接copy小海龟A 拷贝一个小海龟  B   
            Enemy smallSeaTurtleB = (SeaTurtle)smallSeaTurtleA.DeepClone();
            smallSeaTurtleB.Location.X = seaTurtleLocation.X - 1;

            bigSeaTurtle.Show();
            smallSeaTurtleA.Show();
            smallSeaTurtleB.Show();
            Console.ReadLine();

        }
View Code

最终的结果

我是海龟,攻击力:10,速度:10,出生点位:2,2
我是海龟,攻击力:5,速度:10,出生点位:3,2
我是海龟,攻击力:5,速度:10,出生点位:1,2

我在Enemy类中实现了接口ICloneable,大家可以试试用Clone方法会得到什么效果 

 

总结下

优点:

用于创建重复的对象,同时又能保证性能,同时,我们还可以不用构造方法

缺点

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

2、逃避了构造函数的约束,如果我们的构造方法有约定,那因为可以不用到构造方法,所以我们逃避了约定

 

以上就是关于 原型模式 的分享

你们的支持是我写作的动力源泉,请不要吝啬你的点赞,谢谢

 

以上是关于设计模式 5/23 原型模式的主要内容,如果未能解决你的问题,请参考以下文章

架构师内功心法,只是单纯听说过的原型模式详解

设计模式之原型模式(Prototype)详解及代码示例

设计模式课程 设计模式精讲 9-2 原型模式coding

设计模式--原型模式

设计模式原型模式

Java设计模式----原型模式