创建篇-原型模式

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
    

注意:

  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”关键字所触发的实例化操作。

以上是关于创建篇-原型模式的主要内容,如果未能解决你的问题,请参考以下文章

从零开始学习Java设计模式 | 创建型模式篇:原型模式

创建篇-工厂模式

javascript--面向对象(工厂模式,构造函数,原型模式)

21种设计模式

chatGPT教你设计模式[3] ——创建型模式(单例模式建造者模式和原型模式)

chatGPT教你设计模式[3] ——创建型模式(单例模式建造者模式和原型模式)