我们一定对类的实例化比较熟悉,前面我们说的单例、还有3种工厂模式都是通过new
关键字来创建对象,下面我们来了解一种新的对象创建的方式。
1. 定义
原型模式也是一种创建型的设计模式,实现和原理总体比较简单,一句话总结呢,就是可以实现用已有的对象创建新的对象,而不是用类来实例化对象,这样可以起到提升效率的目的。
众所周知,类的实例化可以创建对象,但其实这是一个比较耗时耗力的工作,尤其是在大量实例化对象的业务场景下,可能会对系统的性能造成很大的影响。这时候,原型模式就可以很好的解决问题,用已有的对象来生成对象的副本,这里已有的对象就是原型对象
,副本对象就是拷贝对象
。这样对于那些有非常复杂的初始化的操作,或者是需要消耗大量资源的情况,原型模式的优势就体现出来了。
2. 代码实现
我们来举一个飞机大战的游戏例子,游戏的场是在手机屏幕上方,飞下来很多敌机,而我方战机只有一架,其中敌机的飞行轨迹必须是呈上下直线型的,我方战机可以上下左右移动。因为本篇我们学习原型模式,所以重点关照的是敌机
,我方战机其实可以通过前面讲的单例模式实现。
2.1 实例化方式
假设游戏过程有500架敌机出现,通常情况下,使用实例化创建对象的方式,代码实现可以这样。
public class EnemyPlain {
// x坐标
private int x;
// y坐标
private int y;
// 敌机固定x坐标,只能上下移动
public EnemyPlain(int x) {
this.x = x;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
// 手柄每调用一次setY方法,Y坐标加一
public void setY() {
y ++ ;
}
}
客户端获取敌机的方法如下:
public class Client {
public static void main(String[] args) {
// 500架敌机集合
List<EnemyPlain> enemyPlains = new ArrayList<>();
// 实例化500架敌机
for (int i = 0; i < 500; i++) {
// 随机出现在0~200的坐标内
EnemyPlain enemyPlain = new EnemyPlain(RandomUtil.randomInt(200));
enemyPlains.add(enemyPlain);
}
}
}
这种代码实现的方式是很常见的,但要命的是这500个对象在客户端初始化的时候就会被创建出来,500个对象会占用大量的堆内存空间,这还是定义的对象只有两个属性的前提下。另外,CPU本身就是很宝贵的资源,一次性实例化500个对象,本身也会消耗系统很大的系统资源,极端的情况下会造成游戏界面卡顿,造成不友好的用户体验,下面我们用原型模式来试一下。
2.2 原型模式
原型模式的代码实现,首先把原型类实现java.lang.Clone
接口,接着实现 clone()
方法。
// 1.实现java.lang.Clone接口
public class EnemyPlain implements Cloneable {
// x坐标
private int x;
// y坐标
private int y;
// 敌机固定x坐标,只能上下移动
public EnemyPlain(int x) {
this.x = x;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
// 手柄每调用一次setY方法,Y坐标加一
public void setY() {
y ++ ;
}
public void setX(int x) {
this.x = x;
}
// 重写克隆 clone 方法
@Override
public EnemyPlain clone() throws CloneNotSupportedException {
return (EnemyPlain)super.clone();
}
}
客户端获取原型拷贝副本代码实现:
public class ClientAno {
public static void main(String[] args) throws CloneNotSupportedException {
// 创建原型对象
EnemyPlain enemyPlain = new EnemyPlain(100);
// 存放500架敌机
List<EnemyPlain> list = new ArrayList<>();
// 克隆500架敌机
for (int i = 0; i < 500; i++) {
EnemyPlain clonePlain = enemyPlain.clone();
// 设置横坐标
clonePlain.setX(RandomUtil.randomInt(200));
list.add(clonePlain);
}
}
}
这里需要特别说明,clone()
方法并不是从Cloneable
接口实现来的,而是继承自java.lang.Object
对象。另外,一般在获取克隆对象的时候,可以借助工厂模式一块实现。
3. UML类图
下面,就以上面飞机大战这个游戏的这个例子,画一个原型模式的UML类图。