五、Prototype 原型模式(创建型)
1、意图:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
2、场景:游戏场景中的有很多相似的敌人,它们的技能都一样,但是随着敌人出现的位置不同,这些人的能力不太一样。假设,我们现在需要把三个步兵组成一队,其中还有一个精英步兵,能力特别高。那么,你或许可以创建一个敌人抽象类,然后对于不同能力的步兵创建不同的子类。然后,使用工厂方法等设计模式让调用方依赖敌人抽象类。
问题来了,如果有无数种能力不同步兵,难道需要创建无数子类吗?还有,步兵模型的初始化工作是非常耗时的,创建这么多步兵对象可能还会浪费很多时间。我们是不是可以通过只创建一个步兵原型,然后复制出多个一摸一样的步兵呢?复制后,只需要调整一下这些对象在地图上出现的位置,或者调整一下它们的能力即可。原型模式就是用来解决这个问题的。
示例代码
5、何时采用
-- 从代码角度来说, 如果你希望运行时指定具体类(比如是使用Footman作为敌人还是使用其它),或者你希望避免创建对象时的初始化过程(如果这个过程占用的时间和资源都非常多),或者是希望避免使用工厂方法来实现多态的时候,可以考虑原型模式。
-- 从应用角度来说, 如果你创建的对象是多变化、多等级的产品,或者产品的创建过程非常耗时的时候(比如,有一定的计算量,或者对象创建时需要从网络或数据库中获取一定的数据),或者想把产品的创建独立出去,不想了解产品创建细节的时候可以考虑使用。不得不说,原型模式给了我们多一种创建对象,并且不依赖具体对象的选择。
6、实现要点
-- .NET中使用Object的MemberwiseClone()方法来实现浅拷贝,通过序列化和反序列化实现深拷贝,后者代价比较大,选择何时的拷贝方式。
-- 原型模式同样需要抽象类型和具体类型,通过相对稳定的抽象类型来减少或避免客户端的修改可能性。
-- 在代码中,我们把敌人作为了抽象类型,抽象层次很高。完全可以把步兵作为抽象类型,下面有普通步兵,手榴弹步兵等等,再有一个坦克作为抽象类型,下面还有普通坦克和防导弹坦克。这样GameScene可能就需要从两种抽象类型克隆出许多步兵和坦克。不管怎么样抽象,只要是对象类型由原型实例所指定,新对象通过原型实例做拷贝,那么这就是原型模式。
7、注意事项
-- 注意选择深拷贝和浅拷贝。
-- 拷贝原型并进行修改意味着原型需要公开更多的数据,对已有系统实现原型模式可能修改的代价比较大。