小话设计模式(五)原型模式
来源:互联网 发布:网络售药 京东 编辑:程序博客网 时间:2024/06/05 19:08
原型(Prototype)模式用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
简单举个例子,游戏里面有动物类型,定义如下:
public class AnimalPrototype{public string name { get; set;}public int hp{get;set;}public AnimalPrototype(string name_){name = name_;}public override string ToString(){return string.Format ("[Animal: name={0}, hp={1}]", name, hp);}}
创建时:
AnimalPrototype wolverine = new AnimalPrototype ("wolverine");wolverine.hp = 100;
这里只有两个属性,name和hp,实际情况可能有十几个或者更多的属性(例如怒气值,是否愤怒,是否遭遇过等等)。我们不希望在创建狼獾(wolverine)时反复设置这些属性,或者有时候我们需要创建一个临时的拷贝了所有属性的wolverine(例如预览伤害时,我们需要生成一个临时的拷贝对象,用于计算预览值),这样的情况下,原型模式是一种不错的选择。
稍微修改一下代码,使用.Net为我们提供的ICloneable接口(这个接口经常见到啊!原来还是一种设计模式!突然感觉高贵冷艳了起来。):
public class AnimalPrototype : ICloneable{public string name { get; set;}public int hp{get;set;}public AnimalPrototype(string name_){name = name_;}public override string ToString(){return string.Format ("[Animal: name={0}, hp={1}]", name, hp);}public virtual object Clone(){return (object)this.MemberwiseClone ();}}
使用时:
AnimalPrototype wolverine2 = (AnimalPrototype)wolverine.Clone ();wolverine2.hp = 80;
这样我们就可以进行各种克隆了,生成茫茫多的wolverine。
然而,这并不能满足我们的需求。例如,我们定义了一种会使用技能的动物:
public class AnimalSkill : ICloneable{public string skillName { get; set;}public int skillAttack { get; set;}public object Clone(){return (object)this.MemberwiseClone ();}public override string ToString (){return string.Format ("[AnimalSkill: skillName={0}, skillAttack={1}]", skillName, skillAttack);}}
public class SkillAnimalPrototype : AnimalPrototype{private AnimalSkill _skill;public AnimalSkill skill { get{ return _skill;}}public SkillAnimalPrototype(string name_):base(name_){_skill = new AnimalSkill ();}public void SetSkillInfo(string name_, int attack_){_skill.skillName = name_;_skill.skillAttack = attack_;}public override object Clone(){return (object)this.MemberwiseClone ();}public override string ToString(){return base.ToString () + skill.ToString ();}}
当我们调用时:
SkillAnimalPrototype phoenix = new SkillAnimalPrototype ("phoenix");phoenix.SetSkillInfo ("Revive", 0);SkillAnimalPrototype phoenix2 = (SkillAnimalPrototype)phoenix.Clone ();phoenix2.name = "phoenix2";phoenix2.SetSkillInfo ("Fire", 10);
Console.WriteLine (phoenix);Console.WriteLine (phoenix2);
会得到结果:
[Animal: name=phoenix, hp=0][AnimalSkill: skillName=Fire, skillAttack=10]
[Animal: name=phoenix2, hp=0][AnimalSkill: skillName=Fire, skillAttack=10]
我们看到AnimalSkill都显示的是第二次设置的信息。是因为MemberwiseClone只是浅表复制,复制值类型没有问题,但是复制引用类型的时候,只是复制了引用(对string做了特殊处理),所以两个AnimalSkill指向的是同一个对象。
所以我们需要这样修改SkillAnimalPrototype的Clone方法:
public override object Clone(){SkillAnimalPrototype ret = (SkillAnimalPrototype)base.Clone();ret._skill = (AnimalSkill)_skill.Clone ();return ret;}
这样我们就会得到新的结果:
[Animal: name=phoenix, hp=0][AnimalSkill: skillName=Revive, skillAttack=0]
[Animal: name=phoenix2, hp=0][AnimalSkill: skillName=Fire, skillAttack=10]
这样就是深表复制。
深表复制比较复杂,实际情况中要格外注意。而且,并不是所有的引用类型的变量都需要被深表复制,例如我们有一个AnimalBasicData的类型:
public class AnimalBasicData{public int mapHp{ get; private set;}public void LoadFromData(string file){//TODO:}}
通过LoadFromData加载配置文件的数据,而我们希望所有的wolverine都用同一份AnimalBasicData,所有的phoenix都用另一份AnimalBasicData,Clone的时候就不需要也不该对AnimalBasicData进行深表复制。
最后说两句:
原型模式好处在于对于相同(或者大部分相似)的对象,我们创建它们的时候,并不需要考虑每一个对象的每一个细节。并且可以创建一个对象的副本,在有限的范围内修改这个副本不会影响原对象。
坏处就在于,我们需要实现复杂的Clone方法,需要避免多余的深表复制(浪费内存),尤其要注意循环引用的情况(例如A引用B,B再引用A,这样会造成死循环)。
- 小话设计模式(五)原型模式
- 设计模式 (五)原型模式(Prototype)
- 设计模式 (五)原型模式(Prototype)
- 设计模式 (五)原型模式(Prototype)
- 设计模式学习(五)原型模式
- Java设计模式(五)----原型模式
- 五。设计模式(原型模式)
- 设计模式(五)原型模式
- 设计模式(五):原型模式
- 设计模式(五) 原型模式
- 小话设计模式六:原型模式
- java设计模式(五)创建者模式和原型模式
- 设计模式(五)创建者模式和原型模式
- 浅谈Java设计模式(五)原型模式(Prototype)
- 设计模式学习日志五:原型模式(原文转载)
- 创建型:设计模式之原型模式(五)
- javascript设计模式介绍(五)动态原型模式
- JAVA--原型模式(Prototype)--设计模式五
- JAVA语言遗漏
- A*算法 学习记录
- 【软工】初识软工-《软件工程导论》
- 我的博文之路由此开启
- |洛谷|分治|P1010 幂次方
- 小话设计模式(五)原型模式
- UGUI研究院之不添加摄像机解决UI与UI特效叠层问题
- [深度学习基础] 1. 图像识别问题的挑战及数据驱动过程
- 5-2 派生类的构造函数
- 大数据之Linux+大数据开发篇
- 连续工作七天之后的说~
- (笔记)Spring实战_最小化Spring XML配置(4)_使用Spring基于Java的配置
- 文章标题 HDU 1902 :The Dragon of Loowater(贪心)
- stm32