创建篇-原型模式


前言

原型模式(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
    }
}

注意:

  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();
    }
}

要点:

  1. 重写父类的克隆方法,这样一来就可以对本类实例进行克隆操作,省去了由类而生的再造过程。
  2. 我们加入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;
    }
}

要点:

  1. 提供了一个获取敌机实例的方法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;
    }
}

要点:

  1. 我们对敌机子弹bullet也进行了克隆,这就是深拷贝操作。(反序列化可以进行深拷贝)

总结

提示:这里对文章进行总结:

  1. 从类到对象叫作“创建”,而由本体对象至副本对象则叫作“克隆”,当需要创建多个类似的复杂对象时,我们就可以考虑用原型模式。究其本质,克隆操作时Java虚拟机会进行内存操作,直接拷贝原型对象数据流生成新的副本对象,绝不会拖泥带水地触发一些多余的复杂操作(如类加载、实例化、初始化等),所以其效率远远高于“new”关键字所触发的实例化操作。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhixuChen200

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值