创建篇-原型模式
Posted zhixuChen333
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了创建篇-原型模式相关的知识,希望对你有一定的参考价值。
文章目录
前言
原型模式(Prototype),在制造业中通常是指大批量生产开始之前研发出的概念模型,并基于各种参数指标对其进行检验,如果达到了质量要求,即可参照这个原型进行批量生产。原型模式达到以原型实例创建副本实例的目的即可,并不需要知道其原始类,也就是说,原型模式可以用对象创建对象,而不是用类创建对象,以此达到效率的提升。
提示:以下是本篇文章正文内容,下面案例可供参考
一、飞机大战
在游戏飞机大战中不停的有敌机从屏幕的上方随机出现,因此我们需要构造大量的敌机实例,接下来我们由浅入深地写下代码。
1.敌机类
public class EnemyPlane
/**
* 横坐标
*/
private int x;
/**
* 纵坐标,敌机一直从最上面出现所以初始化为0
*/
private int y = 0;
public EnemyPlane(int x)
this.x = x;
public int getX()
return x;
public int getY()
return y;
public void fly()
y++; //飞机起飞,调用一次,飞机纵坐标加1
注意:
- 我们只提供了getter方法而没有提供setter方法,也就是说我们在初始化的时候姐确定好敌机的横坐标,之后不允许更改坐标了。
2.客户端类
在客户端类中使用“new”关键字来实例化敌机,并且我们一次性初始化了500个敌机。这样的作法看似没有问题,然而效率却是非常低的。我们知道在游戏画面上根本没必要同时出现这么多敌机,而在游戏还未开始之前,也就是游戏的加载阶段我们就实例化了这一关卡的所有500架敌机,这不但使加载速度变慢,而且是对有限内存资源的一种浪费。那么到底什么时候去构造敌机?答案当然是懒加载了,也就是按照地图坐标,屏幕滚动到某一点时才实时构造敌机,这样一来问题就解决了。
然而遗憾的是,懒加载依然会有性能问题,主要原因在于我们使用的“new”关键字进行的基于类的实例化过程,因为每架敌机都进行全新构造的做法是不合适的,其代价是耗费更多的CPU资源,尤其在一些大型游戏中,很多个线程在不停地运转着,CPU资源本身就非常宝贵,此时若进行大量的类构造与复杂的初始化工作,必然会造成游戏卡顿,甚至有可能会造成系统无响应,使游戏体验大打折扣
public class Client
public static void main(String[] args)
List<EnemyPlane> list = new ArrayList<>();
for (int i = 0; i < 500; i++)
//随机横坐标出现敌机
EnemyPlane ep = new EnemyPlane(new Random().nextInt(200));
list.add(ep);
二、使用clone()方法来进行拷贝
1.可被克隆的敌机类
public class EnemyPlane1 implements Cloneable
/**
* 横坐标
*/
private int x;
/**
* 纵坐标,敌机一直从最上面出现所以初始化为0
*/
private int y = 0;
public EnemyPlane1(int x)
this.x = x;
public int getX()
return x;
public int getY()
return y;
/**
* 加入set方法,为了让克隆后的实例修改横坐标
*/
public void setX(int x)
this.x = x;
public void fly()
y++; //飞机起飞,调用一次,飞机纵坐标加1
/**
* 重写克隆方法
*/
@Override
protected EnemyPlane1 clone() throws CloneNotSupportedException
return (EnemyPlane1)super.clone();
要点:
- 重写父类的克隆方法,这样一来就可以对本类实例进行克隆操作,省去了由类而生的再造过程。
- 我们加入set方法,为了保证克隆飞机的坐标位置个性化。
2.克隆工厂
public class EnemyPlaneFactory
/**
* 此处用单例饿汉模式造一个敌机的原型
*/
private static EnemyPlane1 ep1 = new EnemyPlane1(200);
/**
* 获取敌机的克隆实例
*
* @param x
* @return
* @throws CloneNotSupportedException
*/
public static EnemyPlane1 getInstance(int x) throws CloneNotSupportedException
EnemyPlane1 ep = ep1.clone();
ep.setX(x);
return ep;
要点:
- 提供了一个获取敌机实例的方法getInstance(),其中简单地调用克隆方法得到一个新的克隆对象,并将其横坐标重设为传入的参数,最后返回此克隆对象,这样我们便可轻松获取一架敌机的克隆实例了。
3.深拷贝和浅拷贝
我们都知道,Java中的变量分为原始类型和引用类型,所谓浅拷贝是指只复制原始类型的值,比如横坐标x与纵坐标y这种以原始类型int定义的值,它们会被复制到新克隆出的对象中。而引用类型bullet同样会被拷贝,但是请注意这个操作只是拷贝了地址引用(指针),也就是说副本敌机与原型敌机中的子弹是同一颗,因为两个同样的地址实际指向的内存对象是同一个bullet对象。
public class EnemyPlane2 implements Cloneable
/**
* 子弹
*/
private Bullet bullet;
/**
* 横坐标
*/
private int x;
/**
* 纵坐标,敌机一直从最上面出现所以初始化为0
*/
private int y = 0;
public EnemyPlane2(int x)
this.x = x;
public int getX()
return x;
public int getY()
return y;
public Bullet getBullet()
return bullet;
/**
* 加入set方法,为了让克隆后的实例修改横坐标
*/
public void setX(int x)
this.x = x;
public void fly()
y++; //飞机起飞,调用一次,飞机纵坐标加1
public void setBullet(Bullet bullet)
this.bullet = bullet;
/**
* 重写克隆方法
*/
@Override
protected EnemyPlane2 clone() throws CloneNotSupportedException
EnemyPlane2 enemyPlane2 = (EnemyPlane2)super.clone();
enemyPlane2.setBullet(this.bullet.clone());
return enemyPlane2;
要点:
- 我们对敌机子弹bullet也进行了克隆,这就是深拷贝操作。(反序列化可以进行深拷贝)
总结
提示:这里对文章进行总结:
- 从类到对象叫作“创建”,而由本体对象至副本对象则叫作“克隆”,当需要创建多个类似的复杂对象时,我们就可以考虑用原型模式。究其本质,克隆操作时Java虚拟机会进行内存操作,直接拷贝原型对象数据流生成新的副本对象,绝不会拖泥带水地触发一些多余的复杂操作(如类加载、实例化、初始化等),所以其效率远远高于“new”关键字所触发的实例化操作。
以上是关于创建篇-原型模式的主要内容,如果未能解决你的问题,请参考以下文章
javascript--面向对象(工厂模式,构造函数,原型模式)