设计模式之禅之设计模式-原型模式

设计模式之禅之设计模式-原型模式

一:原型模式的定义

​ 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

​ 原型模式(Prototype Pattern)的简单程度仅次于单例模式和迭代器模式。正是由于简单,使用的场景才非常地多。

​ 原型模式的核心是一个clone方法,通过该方法进行对象的拷贝,Java提供了一个Cloneable接口来标示这个对象是可拷贝的,为什么说是"标示"呢?翻开JDK的帮助看看Cloneable是一个方法都没有的,这个接口只是一个标记作用,在JVM中具有这个标记的对象才有可能被拷贝。那怎么才能从"有可能被拷贝"转换为"可以被拷贝"呢?方法是覆盖clone()方法,是的,你没有看错是重写clone()方法,看看我们上面Mail类中的clone方法.该方法重写了Object对象的方法

二:原型模式的优点

​ ● 性能优良

​ 原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。

​ ● 逃避构造函数的约束

​ 这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的(参见13.4节)。优点就是减少了约束,缺点也是减少了约束,需要大家在实际应用时考虑。

三:原型模式的应用场景

​ ● 资源优化场景

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

​ ● 性能和安全要求的场景

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

​ ● 一个对象多个修改者的场景

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

​ 在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。原型模式已经与Java融为一体,大家可以随手拿来使用。

四:原型模式的注意事项

构造函数不会被执行

​ 注意浅拷贝。你可能会比较奇怪,为什么在Mail那个类中就可以使用String类型,而不会产生由浅拷贝带来的问题呢?内部的数组和引用对象才不拷贝,其他的原始类型比如int、long、char等都会被拷贝,但是对于String类型,Java就希望你把它认为是基本类型,它是没有clone方法的,处理机制也比较特殊,通过字符串池(stringpool)在需要的时候才在内存中创建新的字符串,读者在使用的时候就把String当做基本类使用即可。

​ 使用原型模式时,引用的成员变量必须满足两个条件才不会被拷贝:一是类的成员变量,而不是方法内变量;二是必须是一个可变的引用对象,而不是一个原始类型或不可变对象。

​ 深拷贝还有一种实现方式就是通过自己写二进制流来操作对象,然后实现对象的深拷贝,这个大家有时间自己实现一下

​ 深拷贝和浅拷贝建议不要混合使用,特别是在涉及类的继承时,父类有多个引用的情况就非常复杂,建议的方案是深拷贝和浅拷贝分开实现。

五:clone和fianl两个冤家

​ 你要实现深拷贝的梦想在final关键字的威胁下破灭了,路总是有的,我们来想想怎么修改这个方法:删除掉final关键字,这是最便捷、安全、快速的方式

​ 你要使用clone方法,在类的成员变量上就不要增加final关键字。

六:原型模式最佳实践

​ 可以这样理解:一个对象的产生可以不由零起步,直接从一个已经具备一定雏形的对象克隆,然后再修改为生产需要的对象。也就是说,产生一个人,可以不从1岁长到2岁,再到3岁...也可以直接找一个人,从其身上获得DNA,然后克隆一个,直接修改一下就是30岁了!我们讲的原型模式也就是这样的功能

七:原型模式例子

【1】原型模式的模板

复制代码
package com.javagpt.design;

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

/**
 * 深层次拷贝
 * (1)深拷贝还有一种实现方式就是通过自己写二进制流来操作对象,然后实现对象的深拷贝,这个大家有时间自己实现一下
 * (2)深拷贝和浅拷贝建议不要混合使用,特别是在涉及类的继承时,父类有多个引用的情况就非常复杂,建议的方案是深拷贝和浅拷贝分开实现。
 *
 * @author javagpt
 */
public class Thing2 implements Cloneable {
    private ArrayList<String> list = new ArrayList<String>();


    @Override
    protected Thing2 clone() throws CloneNotSupportedException {
        Thing2 thing2 = null;
        thing2 = (Thing2) super.clone();
        thing2.list = (ArrayList<String>) this.list.clone();
        return thing2;
    }

    public List<String> getList() {
        return list;
    }

    public void setList(String a) {
        this.list.add(a);
    }

}

【2】浅拷贝

复制代码
package com.javagpt.design;

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

/**
 * 浅拷贝
 *
 * @author javagpt
 * (1)JVM做了一个偷懒的拷贝动作,Object类提供的方法clone只是拷贝本对象,其对象内部的数组、引用对象等都不拷贝,还是指向原生对象
 * 的内部元素地址,这种拷贝就叫做浅拷贝
 * (2)非常不安全
 */
public class Thing implements Cloneable {

    private List<String> list = new ArrayList<String>();


    @Override


    protected Thing clone() throws CloneNotSupportedException {
        Thing thing = null;
        thing = (Thing) super.clone();
        return thing;
    }

    public List<String> getList() {
        return list;
    }

    public void setList(String a) {
        this.list.add(a);
    }

}

【3】深拷贝

复制代码
package com.javagpt.design;

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

/**
 * 深层次拷贝
 * (1)深拷贝还有一种实现方式就是通过自己写二进制流来操作对象,然后实现对象的深拷贝,这个大家有时间自己实现一下
 * (2)深拷贝和浅拷贝建议不要混合使用,特别是在涉及类的继承时,父类有多个引用的情况就非常复杂,建议的方案是深拷贝和浅拷贝分开实现。
 *
 * @author javagpt
 */
public class Thing2 implements Cloneable {
    private ArrayList<String> list = new ArrayList<String>();

    @Override
    protected Thing2 clone() throws CloneNotSupportedException {
        Thing2 thing2 = null;
        thing2 = (Thing2) super.clone();
        thing2.list = (ArrayList<String>) this.list.clone();
        return thing2;
    }

    public List<String> getList() {
        return list;
    }

    public void setList(String a) {
        this.list.add(a);
    }

}

【4】客户端测试

复制代码
package com.javagpt.design;

import java.util.List;


public class ClientTest {

    public static void main(String[] args) throws CloneNotSupportedException {
        //test01();
        //test02();
        test03();
    }

    /**
     * 原型模式:模板测试
     *
     * @throws CloneNotSupportedException
     */
    public static void test01() throws CloneNotSupportedException {
        Mail mail = new Mail("javagpt", "go smx", "emailtojavagpt", "javagpt@163.com");//ClientTest.main()com.javagpt.design.Mail@2a5330
        System.out.println("ClientTest.main()" + mail.toString());
        Mail mail2 = mail.clone();
        System.out.println("ClientTest.main()" + mail2.toString());//ClientTest.main()com.javagpt.design.Mail@18872380

    }

    /**
     * 原型模式:浅拷贝
     *
     * @throws CloneNotSupportedException
     */
    public static void test02() throws CloneNotSupportedException {
        Thing thing1 = new Thing();
        thing1.setList("小李");
        Thing thing2 = thing1.clone();
        thing1.setList("小张");
        List<String> t = thing1.getList();
        List<String> t2 = thing2.getList();
        for (int i = 0; i < t.size(); i++) {
            System.out.println("ClientTest.test02(t==>)" + t.get(i));
        }
        for (int i = 0; i < t2.size(); i++) {
            System.out.println("ClientTest.test02(t2==>)" + t2.get(i));
        }
        //ClientTest.test02(t==>)小李
        //ClientTest.test02(t==>)小张
        //ClientTest.test02(t2==>)小李
        //ClientTest.test02(t2==>)小张
    }

    /**
     * 原型模式:深拷贝
     *
     * @throws CloneNotSupportedException
     */
    public static void test03() throws CloneNotSupportedException {
        Thing2 thing2a = new Thing2();
        thing2a.setList("小李");
        Thing2 thing2b = thing2a.clone();
        thing2a.setList("小张");
        List<String> t = thing2a.getList();
        List<String> t2 = thing2b.getList();
        for (int i = 0; i < t.size(); i++) {
            System.out.println("ClientTest.test02(t==>)" + t.get(i));
        }
        for (int i = 0; i < t2.size(); i++) {
            System.out.println("ClientTest.test02(t2==>)" + t2.get(i));
        }
        //ClientTest.test02(t==>)小李
        //ClientTest.test02(t==>)小张
        //ClientTest.test02(t2==>)小李
    }
}
相关推荐
杨充10 小时前
10.可测试性实战设计
设计模式·开源·代码规范
杨充10 小时前
9.重构十二式的实战
设计模式·开源·代码规范
杨充10 小时前
6.设计原则的全景图
设计模式·开源·全栈
杨充10 小时前
2.面向对象的特性
设计模式
杨充10 小时前
7.SOLID原则案例汇
设计模式·开源·全栈
杨充10 小时前
8.反模式与坏味道
设计模式·开源·代码规范
杨充10 小时前
3.接口vs抽象类比较
设计模式
咖啡八杯13 小时前
文法、BNF与AST
java·设计模式·解释器模式·ast·文法
咖啡八杯1 天前
GoF设计模式——解释器模式
java·后端·spring·设计模式