问题
一个游戏程序例如微信的打飞机游戏,游戏中需要多个敌机实例,假如需要1000个敌机实例对象,我们该怎么创建这些对象?
一种可行的做法是:游戏还未开始之前,也就是游戏的加载阶段我们就实例化了这一关卡的所有1000架敌机,这种做法看似没有任何问题,然而效率却是非常低的。我们知道在游戏画面上根本没必要同时出现这么多敌机,而在游戏还未开始之前,也就是游戏的加载阶段我们就实例化了这一关卡的所有1000架敌机,这不但使加载速度变慢,而且是对有限内存资源的一种浪费。
另一种方法是:“懒加载”,随着游戏的进行,需要新的敌机出现时,再New一个敌机实例出来。然而这种方法依然可能有性能问题,基于类的实例化过程需要调用类的构造方法,如果这个构造方法很复杂或者需要大量的CPU资源(在大型游戏中,这个假设是很有可能的),那么可能会造成游戏卡顿。
一个很好的解决方法是使用——原型模式
定义
原型模式(Prototype Pattern):使用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型模式是一种对象创建型模式。
说人话就是:用实例对象创建实例对象。
这么做的好处在于:当一个类的初始化方法很复杂或者需要耗费大量CPU资源,并且我们还需要多个这个类的对象时,用实例对象克隆出新的实例对象的方法可以节省资源,提高系统的性能。
原型模式
我们继续以打飞机游戏为例,运用原型模式的做法是,敌机类提供一个克隆自己的接口,具体看代码:
1 | //EnemyPlane敌机类实现了java.lang包中的克隆接口Cloneable |
深拷贝与浅拷贝
上述拷贝方法有一个问题,如果上述敌机类有一个属性是引用类型,比如属性是另一个类的实例化对象,如敌机类有一个子弹属性private Bullet bullet = new Bullet();
Java中的变量分为原始类型和引用类型,所谓浅拷贝是指只复制原始类型的值,比如横坐标x与纵坐标y这种以原始类型int定义的值,它们会被复制到新克隆出的对象中。而引用类型同样会被拷贝,但是请注意这个操作只是拷贝了地址引用(指针),也就是说克隆出的副本敌机与原型敌机中的子弹是同一颗,因为两个同样的地址实际指向的内存对象是同一个bullet对象。
那么上述代码的克隆方法就需要修改一下了:
1 | //EnemyPlane敌机类实现了java.lang包中的克隆接口Cloneable |
Python实现原型模式
参考:python-原型模式
参考文献:
《秒懂设计模式》刘韬