【设计模式-2.3】创建型——原型模式
  TEZNKK3IfmPf 24天前 20 0

说明:本文介绍设计模式中,创建型中的原型模式;

飞机大战

创建型设计模式关注于对象的创建,原型模式也不例外。如简单工厂和工厂模式中提到过的飞机大战这个例子,游戏中飞机、坦克对象会创建许许多多的实例,每个实例除了坐标,是一模一样的,如果每次都用关键字new去创建,是非常消耗时间的。

【设计模式-2.3】创建型——原型模式

(Enemy,敌人抽象类)

/** * 敌人抽象类 */
public abstract class Enemy {
     
       

    /** * 敌人的坐标 */
    protected int x;

    /** * 敌人的坐标 */
    protected int y;

    /** * 抽象方法 */
    public Enemy(int x, int y) {
     
       
        this.x = x;
        this.y = y;
    }

    /** * 绘制方法 */
    public abstract void show();
}

(AirPlane,飞机类)

/** * 飞机 */
public class AirPlane extends Enemy {
     
       

    public AirPlane(int x, int y) {
     
       
        super(x, y);
    }

    @Override
    public void show() {
     
       
        System.out.println("飞机出现了,坐标是:" + x + "," + y);
    }
}

(Client,客户端类,循环创建对象,浪费资源,影响效率

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/** * 客户端 */
public class Client {
     
       
    public static void main(String[] args) {
     
       
        // 屏幕宽度
        int screenLength = 100;

        // 定义飞机集合
        List<AirPlane> airPlanes = new ArrayList<>();

        // 创建飞机
        for (int i = 0; i < 100000; i++) {
     
       
            airPlanes.add(new AirPlane(new Random().nextInt(screenLength), 0));
        }
    }
}

原型模式

原型模式,是使用了Object类里面的clone()方法,对原型对象,也就是创建的第一个对象进行了克隆,避免重复创建对象。如下:

(AirPlane,飞机类,实现Cloneable接口)

/** * 飞机 */
public class AirPlane extends Enemy implements Cloneable {
     
       

    public AirPlane(int x, int y) {
     
       
        super(x, y);
    }
    
    @Override
    public void show() {
     
       
        System.out.println("飞机出现了,坐标是:" + x + "," + y);
    }

    /** * clone方法,调用父类的clone方法 */
    public AirPlane clone() {
     
       
        Object obj = null;
        try {
     
       
            obj = super.clone();
            return (AirPlane) obj;
        } catch (CloneNotSupportedException e) {
     
       
            System.out.println("克隆失败");
            return null;
        }
    }
}

(AirPlaneFactory,飞机工厂类,调用飞机的克隆方法)

/** * 飞机工厂 */
public class AirPlaneFactory {
     
       

    /** * 加载一个原型对象 */
    private static AirPlane airPlane = new AirPlane(0, 0);

    /** * 获取飞机 * @param x * @return */
    public static AirPlane getAirPlane(int x) {
     
       
        AirPlane clone = airPlane.clone();
        clone.setX(x);
        return clone;
    }
}

(客户端类,创建飞机实例对象)

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/** * 客户端 */
public class Client {
     
       
    public static void main(String[] args) {
     
       
        // 屏幕宽度
        int screenLength = 100;

        // 定义飞机集合
        List<AirPlane> airPlanes = new ArrayList<>();

        // 创建飞机
        for (int i = 0; i < 100000; i++) {
     
       
            airPlanes.add(AirPlaneFactory.getAirPlane(new Random().nextInt(screenLength)));
        }

        // 比较两个飞机是否相等
        System.out.println(airPlanes.get(0) == airPlanes.get(1));
    }
}

克隆的实例,是不相等的

【设计模式-2.3】创建型——原型模式

以上就是通过原型模式实例化对象的过程,可以节约内存空间。

浅拷贝和深拷贝

在使用了clone()方法时,我们需要知道浅拷贝和深拷贝的概念。在Java中有基本数据类型和引用数据类型,其中基本数据类型有8种,分别是byte、short、int、long、float、double、char、boolean,除此之外的都是引用数据类型。

对于基本数据类型,浅拷贝和深拷贝都是创建一个新对象,而对于引用数据类型,浅拷贝是拷贝一个对象的指针,深拷贝是拷贝一个一模一样的对象

例如,在上面的基础上,我创建一个机长类,在飞机类里面注入一个机长类对象,克隆之后,对飞机里面的机长对象进行判断,如下:

(机长类)

/** * 机长 */
public class Captain implements Cloneable{
     
       

    /** * 姓名 */
    private String name;

    /** * 年龄 */
    private Integer age;

    public String getName() {
     
       
        return name;
    }

    public void setName(String name) {
     
       
        this.name = name;
    }

    public Integer getAge() {
     
       
        return age;
    }

    public void setAge(Integer age) {
     
       
        this.age = age;
    }
}

(注入一个机长类对象)

/** * 飞机 */
public class AirPlane extends Enemy implements Cloneable {
     
       

    /** * 机长 */
    private Captain captain;

    public AirPlane(int x, int y, Captain captain) {
     
       
        super(x, y);
        this.captain = captain;
    }

    @Override
    public void show() {
     
       
        System.out.println("飞机出现了,坐标是:" + x + "," + y);
    }

    /** * clone方法,调用父类的clone方法 */
    public AirPlane clone() {
     
       
        Object obj = null;
        try {
     
       
            obj = super.clone();
            return (AirPlane) obj;
        } catch (CloneNotSupportedException e) {
     
       
            System.out.println("克隆失败");
            return null;
        }
    }

    public Captain getCaptain() {
     
       
        return captain;
    }
}

(克隆对象后,对飞机对象和飞机对象里面的机长对象进行判断)

	// 比较两个飞机是否相等
	System.out.println(airPlanes.get(0) == airPlanes.get(1));
	
	// 比较两个飞机的机长是否相等
	System.out.println(airPlanes.get(0).getCaptain() == airPlanes.get(1).getCaptain());

结果是飞机对象是互不相同的,但是飞机对象中的引用数据类型,是相同的,是浅拷贝;

【设计模式-2.3】创建型——原型模式

这肯定是不行的,每个实例的数据应该是封装独有的,不能“克隆了,但没完全克隆”。我们可以采用字节流的方式实现“深拷贝”,如下:

(敌人抽象类)

/** * 敌人抽象类 */
public abstract class Enemy {
     
       

    /** * 敌人的坐标 */
    protected int x;

    /** * 敌人的坐标 */
    protected int y;

    public int getX() {
     
       
        return x;
    }

    public void setX(int x) {
     
       
        this.x = x;
    }

    public int getY() {
     
       
        return y;
    }

    /** * 绘制方法 */
    public abstract void show();
}

(飞机类,使用IO流的方式来实现深拷贝)

import java.io.*;

/** * 飞机 */
public class AirPlane extends Enemy implements Serializable {
     
       

    /** * 机长 */
    private Captain captain;

    public Captain getCaptain() {
     
       
        return captain;
    }

    public void setCaptain(Captain captain) {
     
       
        this.captain = captain;
    }

    @Override
    public void show() {
     
       
        System.out.println("飞机出现了,坐标是:" + x + "," + y);
    }

    /** * 获取飞机 * @param x * @return */
    public AirPlane getAirPlane(int x) throws IOException, ClassNotFoundException {
     
       
        this.setX(x);

        // 创建对象输出流
        ByteArrayOutputStream bao = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bao);
        oos.writeObject(this);

        // 返回克隆对象
        ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (AirPlane) ois.readObject();
    }
}

(机长类)

import java.io.Serializable;

/** * 机长 */
public class Captain implements Serializable {
     
       

    /** * 姓名 */
    private String name;

    /** * 年龄 */
    private Integer age;

    public String getName() {
     
       
        return name;
    }

    public void setName(String name) {
     
       
        this.name = name;
    }

    public Integer getAge() {
     
       
        return age;
    }

    public void setAge(Integer age) {
     
       
        this.age = age;
    }
}

创建对象,调用“克隆”方法,比较两个对象的地址和对象内的引用类型数据的地址;

import java.io.IOException;
import java.util.Random;

/** * 客户端 */
public class Client {
     
       
    public static void main(String[] args) throws IOException, ClassNotFoundException {
     
       
        // 屏幕宽度
        int screenLength = 100;

        AirPlane airPlane = new AirPlane();

        airPlane.setCaptain(new Captain());

        AirPlane airPlaneNew = airPlane.getAirPlane(new Random().nextInt(screenLength));

        // 比较两个飞机是否相等
        System.out.println(airPlane == airPlaneNew);

        // 比较两个飞机的机长是否相等
        System.out.println(airPlane.getCaptain() == airPlaneNew.getCaptain());
    }
}

都为false,说明“深拷贝”已经实现。

【设计模式-2.3】创建型——原型模式

深克隆的实现并未用到Object的clone方法,而是使用了IO流中的Object流的方式,对对象进行序列化来实现的。

原型管理器

原型管理器的构想是,创建一个管理器类,包含了所有需要克隆的对象,需要的时候可以直接调用管理器中的方法,取一个克隆的对象出来。代码如下:

(定义一个敌人接口,一个克隆方法,一个展示方法)

/** * 敌人接口 */
public interface Enemy extends Cloneable{
     
       

    /** * 克隆方法 * @return */
    Enemy clone();

    /** * 显示方法 */
    void show();
}

(分别定义敌人飞机、敌人坦克对象)

/** * 敌人飞机 */
public class AirPlane implements Enemy{
     
       

    @Override
    public Enemy clone() {
     
       
        AirPlane airPlane = null;
        try {
     
       
            airPlane = (AirPlane) super.clone();
        } catch (CloneNotSupportedException e) {
     
       
            System.out.println("克隆失败");
        }
        return airPlane;
    }

    @Override
    public void show() {
     
       
        System.out.println("敌人飞机");
    }
}

/** * 敌人坦克 */
public class Tank implements Enemy{
     
       
    @Override
    public Enemy clone() {
     
       
        Tank tank = null;
        try {
     
       
            tank = (Tank) super.clone();
        } catch (CloneNotSupportedException e) {
     
       
            System.out.println("克隆失败");
        }
        return tank;
    }

    @Override
    public void show() {
     
       
        System.out.println("敌人坦克");
    }
}

(定义原型管理器,通过getKey()的方式获取到对应对象的克隆实例)

import java.util.Hashtable;

/** * 原型管理类 */
public class PrototypeManagement {
     
       

    private Hashtable ht = new Hashtable();

    private static PrototypeManagement pm = new PrototypeManagement();

    /** * 添加敌人 * @param key * @param enemy */
    public void addEnemy(String key, Enemy enemy) {
     
       
        ht.put(key, enemy);
    }

    /** * 获取敌人 * @param key * @return */
    public Enemy getEnemy(String key) {
     
       
        Enemy enemy = (Enemy) ht.get(key);
        return enemy.clone();
    }

    public static PrototypeManagement getPrototypeManagement() {
     
       
        return pm;
    }
}

(客户端测试)

/** * 原型管理器客户端 */
public class Client {
     
       
    public static void main(String[] args) {
     
       
        PrototypeManagement pm = PrototypeManagement.getPrototypeManagement();

        // 添加敌人
        pm.addEnemy("airplane", new AirPlane());
        pm.addEnemy("tank", new Tank());

        AirPlane airplane = (AirPlane) pm.getEnemy("airplane");
        airplane.show();

        AirPlane airplaneNew = (AirPlane) pm.getEnemy("airplane");
        airplaneNew.show();

        System.out.println(airplane == airplaneNew);

        System.out.println("====================================");

        Tank tank = (Tank) pm.getEnemy("tank");
        tank.show();

        Tank tankNew = (Tank) pm.getEnemy("tank");
        tankNew.show();

        System.out.println(tank == tankNew);
    }
}

通过克隆获取实例,地址不相等;

【设计模式-2.3】创建型——原型模式

此时,如果需要增加一个Boss,非常简单,只需要新增一个类实现Enemy接口即可;

总结

本文参考《设计模式的艺术》、《秒懂设计模式》两书

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 24天前 0

暂无评论

推荐阅读
TEZNKK3IfmPf