GOF(五)-原型模式【推荐】

原型模式(Prototype Pattern)

原型模式主要用于创建重复的对象,同时又能保证性能,所以是创建型


在使用原型模式时,我们需要首先创建一个原型对象,再通过复制这个原型对象,来创建更多同类型的对象。

使用原型复制对象,性能很高,所以常用于创建大对象,或者初始化繁琐的对象:比如游戏里面的地图等等。

适用场景:

  • 一是类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等;

  • 二是通过 new 产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式;

  • 三是一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。

注意:原型模式很少单独出现,一般是和工厂方法模式一起出现,通过 clone的方法创建一个对象,然后由工厂方法提供给调用者。

UML的相关知识,可以访问我的另外一篇博文
在这里插入图片描述
网上的接口继承的方式实现起来较为简单:
通过ClassA implements Cloneable ,然后加入clone()方法即可【但是这是 浅拷贝

    public Object clone() {
        Object clone = null;
        try {
            clone = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }

测试代码:

// ClassA 实现了 Cloneable 接口
ClassA a = new ClassA();
a.setXXX();

ClassA b = (ClassA) c.clone();
b.setXXX();

以上方法不过多赘述


这篇文章主要通过调用对象,实现浅拷贝和深拷贝。
在这里插入图片描述

看完图说理论:

浅Copy是指被复制的对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都指向原来的对象。简单点说就是,只复制了引用,而没有复制真正的对象内容。

在这里插入图片描述

深Copy是指被复制的对象的所有变量都含有与原来对象相同的值,属性中的对象都指向被复制过的新对象中属性,而不再是原型对象中的属性。简单点说,就是深Copy把所有的对象的引用以及对象都复制了一遍,在堆中是存在两个相互独立的对象,以及属性中的对象也是相互独立的。


说完了浅拷贝深拷贝,正式开始编码,还是拿游戏地图举例子

非常重要:大家着重看clone()方法

首先实现浅拷贝(Shallow Copy):

总地图GameMap代码:

public class ShallowCopyGameMap {

    private String tree;

    private String mountain;

    public String getTree() {
        return tree;
    }

    public void setTree(String tree) {
        this.tree = tree;
    }

    public String getMountain() {
        return mountain;
    }

    public void setMountain(String mountain) {
        this.mountain = mountain;
    }

    public Object clone() {
        Object o = null;
        try {
            o = super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println(e.toString());
        }
        return o;
    }

    @Override
    public String toString() {
        return "{" +
                "tree='" + tree + '\'' +
                ", mountain='" + mountain + '\'' +
                '}';
    }
}

LOL游戏地图LOLGameMap如下:

/**
 * @description:
 * 浅拷贝
 */
public class ShallowCopyLOLGameMap implements Cloneable {

    private String dragon;

    private ShallowCopyGameMap gameMap; // 关联GameMap对象


    public ShallowCopyLOLGameMap(String dragon, 
    						ShallowCopyGameMap gameMap) {
        this.dragon = dragon;
        this.gameMap = gameMap;
    }

    public String getDragon() {
        return dragon;
    }

    public void setDragon(String dragon) {
        this.dragon = dragon;
    }

    public ShallowCopyGameMap getGameMap() {
        return gameMap;
    }

    public void setGameMap(String tree, String mountain) {
        gameMap.setTree(tree);
        gameMap.setMountain(mountain);
    }

    public Object clone() {
        Object clone = null;
        try {
            clone = super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return clone;
    }

    @Override
    public String toString() {
        return "{" +
                "dragon='" + dragon + '\'' +
                ", gameMap=" + gameMap +
                '}';
    }
}

这两个类基本相似,都实现了Cloneable接口

接下来进行测试:

public static void main(String[] args) {
        System.out.println("浅拷贝测试");
        ShallowCopyGameMap gameMap = new ShallowCopyGameMap();
        ShallowCopyLOLGameMap lolGameMap1 = 
        			new ShallowCopyLOLGameMap("纳什男爵", gameMap);
        lolGameMap1.setGameMap("树","山");

        System.out.println("地图1:" + lolGameMap1);

        ShallowCopyLOLGameMap lolGameMap2 = 
        				(ShallowCopyLOLGameMap) lolGameMap1.clone();
        lolGameMap2.setGameMap("树2", "山2");
        System.out.println("地图2:" + lolGameMap2);


        ShallowCopyLOLGameMap lolGameMap3 = 
        				(ShallowCopyLOLGameMap) lolGameMap2.clone();
        lolGameMap3.setGameMap("树3", "山3");
        System.out.println("地图3:" + lolGameMap3);

        System.out.println("重新查看前两个地图: ");
        System.out.println("地图1:" + lolGameMap1);
        System.out.println("地图2:" + lolGameMap2);
    }

测试结果:

浅拷贝测试
地图1{dragon='纳什男爵', gameMap={tree='树', mountain='山'}}
地图2{dragon='纳什男爵', gameMap={tree='树2', mountain='山2'}}
地图3{dragon='纳什男爵', gameMap={tree='树3', mountain='山3'}}
重新查看前两个地图: 
地图1{dragon='纳什男爵', gameMap={tree='树3', mountain='山3'}}
地图2{dragon='纳什男爵', gameMap={tree='树3', mountain='山3'}}

这就是浅拷贝的测试结果,实际开发中要非常留意这个问题,最好在测试时将之前的对象再进行测试输出一次


我这里提供两种方式实现深拷贝:串行化非串行化

非串行化方式实现:(还是通过实现Cloneable接口,注意这里的clone()方法与浅拷贝的区别)

深拷贝(非串行化):总地图GameMap代码:代码与浅拷贝相同,不过多解释

public class DeepCopyGameMap2 implements Cloneable {

    private String tree;

    private String mountain;

    public String getTree() {
        return tree;
    }

    public void setTree(String tree) {
        this.tree = tree;
    }

    public String getMountain() {
        return mountain;
    }

    public void setMountain(String mountain) {
        this.mountain = mountain;
    }

    public Object clone() {
        Object o = null;
        try {
            o = super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println(e.toString());
        }
        return o;
    }

    @Override
    public String toString() {
        return "{" +
                "tree='" + tree + '\'' +
                ", mountain='" + mountain + '\'' +
                '}';
    }
}

深拷贝(非串行化):LOL游戏地图LOLGameMap如下:

特别注意:注意这里的clone()方法与浅拷贝的区别:

/**
 * @description:
 * 通过转换为二进制进行深拷贝
 */
public class DeepCopyLOLGameMap2 implements Cloneable {

    private String dragon;

    private DeepCopyGameMap2 deepCopyGameMap;


    public DeepCopyLOLGameMap2(String dragon, 
    						DeepCopyGameMap2 deepCopyGameMap) {
        this.dragon = dragon;
        this.deepCopyGameMap = deepCopyGameMap;
    }

    public String getDragon() {
        return dragon;
    }

    public void setDragon(String dragon) {
        this.dragon = dragon;
    }

    public DeepCopyGameMap2 getGameMap() {
        return deepCopyGameMap;
    }

    public void setDeepCopyGameMap2(String tree, String mountain) {
        deepCopyGameMap.setTree(tree);
        deepCopyGameMap.setMountain(mountain);
    }

    // 非串行化方式, 在LOLGameMap的clone()方法中调用总地图的clone()方法
    public DeepCopyLOLGameMap2 clone() {
        DeepCopyLOLGameMap2 clone = null;
        try {
            clone = (DeepCopyLOLGameMap2)super.clone();

        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        assert clone != null; // 非空判断,可以忽略
        // 深浅拷贝的区别在这里!!!!!  
        // 使用了GameMap总地图的clone()方法
        // GameMap的字段也是浅拷贝中出错变化的地方
        clone.deepCopyGameMap = 
        				(DeepCopyGameMap2) deepCopyGameMap.clone();
        return clone;
    }

    @Override
    public String toString() {
        return "{" +
                "dragon='" + dragon + '\'' +
                ", deepCopyGameMap=" + deepCopyGameMap +
                '}';
    }
}

深拷贝(非串行化)测试代码:

public static void main(String[] args) {
        System.out.println("深拷贝测试----  非串行化方式");
        DeepCopyGameMap2 deepCopyGameMap = new DeepCopyGameMap2();
        deepCopyGameMap.setTree("树");
        deepCopyGameMap.setMountain("山");
        DeepCopyLOLGameMap2 lolGameMap1 = 
        		new DeepCopyLOLGameMap2("纳什男爵",deepCopyGameMap);
        lolGameMap1.setDeepCopyGameMap2("树","山");

        System.out.println("地图1:" + lolGameMap1);

        DeepCopyLOLGameMap2 lolGameMap2 = 
        					(DeepCopyLOLGameMap2) lolGameMap1.clone();
        lolGameMap2.setDeepCopyGameMap2("树2", "山2");
        System.out.println("地图2:" + lolGameMap2);


        DeepCopyLOLGameMap2 lolGameMap3 = 
        					(DeepCopyLOLGameMap2) lolGameMap2.clone();
        lolGameMap3.setDeepCopyGameMap2("树3", "山3");
        System.out.println("地图3:" + lolGameMap3);

        System.out.println("重新查看前两个地图: ");
        System.out.println("地图1:" + lolGameMap1);
        System.out.println("地图2:" + lolGameMap2);
    }
}

测试结果如下:

深拷贝测试----  非串行化方式
地图1{dragon='纳什男爵', deepCopyGameMap={tree='树', mountain='山'}}
地图2{dragon='纳什男爵', deepCopyGameMap={tree='树2', mountain='山2'}}
地图3{dragon='纳什男爵', deepCopyGameMap={tree='树3', mountain='山3'}}
重新查看前两个地图: 
地图1{dragon='纳什男爵', deepCopyGameMap={tree='树', mountain='山'}}
地图2{dragon='纳什男爵', deepCopyGameMap={tree='树2', mountain='山2'}}

串行化方式实现:(必须实现Serializable)

深拷贝(串行化):总地图GameMap代码:

public class DeepCopyGameMap implements Serializable {

    private String tree;

    private String mountain;

    public String getTree() {
        return tree;
    }

    public void setTree(String tree) {
        this.tree = tree;
    }

    public String getMountain() {
        return mountain;
    }

    public void setMountain(String mountain) {
        this.mountain = mountain;
    }

    public Object clone() {
        Object o = null;
        try {
            o = super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println(e.toString());
        }
        return o;
    }

    @Override
    public String toString() {
        return "{" +
                "tree='" + tree + '\'' +
                ", mountain='" + mountain + '\'' +
                '}';
    }
}

深拷贝(串行化):LOL游戏地图LOLGameMap如下:

/**
 * @description:
 * 通过串行化方式进行深拷贝
 */
public class DeepCopyLOLGameMap implements Serializable {

    private String dragon;

    private DeepCopyGameMap deepCopyGameMap;


    public DeepCopyLOLGameMap(String dragon, 
    					DeepCopyGameMap deepCopyGameMap) {
        this.dragon = dragon;
        this.deepCopyGameMap = deepCopyGameMap;
    }

    public String getDragon() {
        return dragon;
    }

    public void setDragon(String dragon) {
        this.dragon = dragon;
    }

    public DeepCopyGameMap getDeepCopyGameMap() {
        return deepCopyGameMap;
    }

    public void setDeepCopyGameMap(String tree, String mountain) {
        deepCopyGameMap.setTree(tree);
        deepCopyGameMap.setMountain(mountain);
    }

    // 串行化方式进行深拷贝
    public DeepCopyLOLGameMap clone() {
        DeepCopyLOLGameMap clone = null;
        try {
            //写入当前对象的二进制流
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(this);

            //读出二进制流产生新的对象
            ByteArrayInputStream bais = 
            			new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);

            clone = (DeepCopyLOLGameMap)ois.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return clone;
    }

    @Override
    public String toString() {
        return "{" +
                "dragon='" + dragon + '\'' +
                ", deepCopyGameMap=" + deepCopyGameMap +
                '}';
    }
}

深拷贝(串行化)测试代码:

    public static void main(String[] args) throws CloneNotSupportedException {
        System.out.println("深拷贝测试--- 串行化方式");
        DeepCopyGameMap deepCopyGameMap = new DeepCopyGameMap();
        DeepCopyLOLGameMap lolGameMap1 = 
        		new DeepCopyLOLGameMap("纳什男爵", deepCopyGameMap);
        lolGameMap1.setDeepCopyGameMap("树","山");

        System.out.println("地图1:" + lolGameMap1);

        DeepCopyLOLGameMap lolGameMap2 = 
        					(DeepCopyLOLGameMap) lolGameMap1.clone();
        lolGameMap2.setDeepCopyGameMap("树2", "山2");
        System.out.println("地图2:" + lolGameMap2);


        DeepCopyLOLGameMap lolGameMap3 = 
        					(DeepCopyLOLGameMap) lolGameMap2.clone();
        lolGameMap3.setDeepCopyGameMap("树3", "山3");
        System.out.println("地图3:" + lolGameMap3);

        System.out.println("重新查看前两个地图: ");
        System.out.println("地图1:" + lolGameMap1);
        System.out.println("地图2:" + lolGameMap2);
    }

测试结果如下:

深拷贝测试--- 串行化方式
地图1{dragon='纳什男爵', deepCopyGameMap={tree='树', mountain='山'}}
地图2{dragon='纳什男爵', deepCopyGameMap={tree='树2', mountain='山2'}}
地图3{dragon='纳什男爵', deepCopyGameMap={tree='树3', mountain='山3'}}
重新查看前两个地图: 
地图1{dragon='纳什男爵', deepCopyGameMap={tree='树', mountain='山'}}
地图2{dragon='纳什男爵', deepCopyGameMap={tree='树2', mountain='山2'}}

注意LOLGameMapclone()方法


总结一下:

原型模式通过Object的clone()方法实现,由于是内存操作,无视构造方法和访问权限,直接获取新的对象。但对于引用类型,需使用深拷贝,其它浅拷贝即可。


代码已经上传到Git:请点击访问

如果大家对于原型模式还有更多的使用技巧和使用心得,欢迎评论并在评论中分享自己的链接!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值