zoukankan      html  css  js  c++  java
  • 设计模式 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、逃避了构造函数的约束,如果我们的构造方法有约定,那因为可以不用到构造方法,所以我们逃避了约定

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

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

  • 相关阅读:
    CF741C.Arpa’s overnight party and Mehrdad’s silent entering [构造 二分图染色]
    CF719E. Sasha and Array [线段树维护矩阵]
    洛谷7月月赛
    CF666B. World Tour
    BZOJ4668: 冷战 [并查集 按秩合并]
    水题练习 2
    CF715B. Complete The Graph
    关于最短路、负环、差分约束系统的一点笔记
    关于最小生成树,拓扑排序、强连通分量、割点、2-SAT的一点笔记
    hdu1814 Peaceful Commission
  • 原文地址:https://www.cnblogs.com/LionelMessi/p/7597803.html
Copyright © 2011-2022 走看看