设计模式 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
怎么实现深拷贝,可以最原始的一个一个赋值,但也可以用反射或反序列化方式来实现,因人而异,具体根据具体需求定,寻找代码最合适的方法
浅拷贝:指的是拷贝一个对象时,仅仅拷贝对象的引用进行拷贝,但是拷贝对象和源对象还是引用同一份实体。此时,其中一个对象的改变都会影响到另一个对象。其实我们自己经常浅拷贝,在家,你是个宝宝的身份,在学校,你是学生的身份,在公司面前,你是职员,但如果你生病了,那么你所有身份都会生病,全都反应在你这个实体身上。在.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; } }
重点!!!在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(); } }
这是出生点位类
/// <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; } }
再定义海归野怪
/// <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); } }
比如我们现在要生成一组野怪,我们看看怎么用原型模式实现
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(); }
最终的结果
我是海龟,攻击力:10,速度:10,出生点位:2,2 我是海龟,攻击力:5,速度:10,出生点位:3,2 我是海龟,攻击力:5,速度:10,出生点位:1,2
我在Enemy类中实现了接口ICloneable,大家可以试试用Clone方法会得到什么效果
总结下
优点:
用于创建重复的对象,同时又能保证性能,同时,我们还可以不用构造方法
缺点
1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。
2、逃避了构造函数的约束,如果我们的构造方法有约定,那因为可以不用到构造方法,所以我们逃避了约定
以上就是关于 原型模式 的分享
你们的支持是我写作的动力源泉,请不要吝啬你的点赞,谢谢
以上是关于设计模式 5/23 原型模式的主要内容,如果未能解决你的问题,请参考以下文章