原型模式

一 定义

用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
原型模式也是一种创建型模式,不同的是,新对象的创建不是通过new来创建,而是在有一个原型对象的基础上通过克隆的方式,快速生成一个和原对象一样的实例。

二 使用场景

  • 资源优化:当类的初始化需要消耗非常多的资源时,这个资源包括数据资源,硬件资源等。
  • 性能和安全要求:通过new产生一个对象需要非常繁琐的数据准备和访问权限。
  • 一个对象,多个修改者:一个对象需要提供给其它对象访问,而且各个调用者可能都要修改其值时。

三 模式结构

  • Client:客户端用户
  • Prototype:抽象类或者接口,声明具备clone能力
  • ConcretePrototype:具体的原型类

四 实例

定义一个类,实现简历的功能,然后通过复制的方式,复制简历。

public class Resume implements Cloneable {

    private String name;
    private String birthday;
    private String school;

    /**
     * 构造方法
     * @param name
     */
    public Resume(String name) {
        this.name = name;
    }

    public void setPersonInfo(String birthday, String school) {
        this.birthday = birthday;
        this.school = school;
    }

    public void display(){
        System.out.println("姓名:"+name);
        System.out.println("生日:"+birthday+" 毕业院校:"+school);
    }

    /**
     * 克隆该实例
     * @return
     */
   @Override
    protected Object clone(){
        Resume resume=null;

        try {
            resume= (Resume) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return resume;
    }

}

客户端测试

 Resume resume1=new Resume("Jackson");
        resume1.setPersonInfo("1990-05-23","华中科技大学");

        Resume resume2= (Resume) resume1.clone();

        System.out.println("-------------------- resume1 ----------------------");
        resume1.display();
        System.out.println("-------------------- resume2 ----------------------");
        resume2.display();

浅拷贝和深拷贝

运用原型模式,实际上只是一个浅拷贝,该拷贝并不是将原始文档上的所有字段都重新构造了一份,而是拷贝文档的字段对原始文档字段的引用。
我们在上面代码加一个字段,设置公司的网列表。

public class Resume implements Cloneable {

    private String name;
    private String birthday;
    private String school;

    private List company=new ArrayList<>();

   /**
     * 添加公司
     * @param company
     */
    public void addCompany(String company){
        this.company.add(company);
    }


    public void display(){
        System.out.println("姓名:"+name);
        System.out.println("生日:"+birthday+" 毕业院校:"+school);
        for (String company:company){
            System.out.println("company:"+company);
        }
    }

    /**
     * 克隆该实例
     * @return
     */
    @Override
    protected Object clone(){
        Resume resume=null;

        try {
            resume= (Resume) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return resume;
    }


    public String getName() {
        return name;
    }

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

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }

    public String getSchool() {
        return school;
    }

    public void setSchool(String school) {
        this.school = school;
    }

    public List getCompany() {
        return company;
    }

    public void setCompany(List company) {
        this.company = company;
    }
}

客户端代码如下

 Resume resume1=new Resume();
        resume1.setName("Jackson");
        resume1.setBirthday("1990-05-23");
        resume1.setSchool("华中科技大学");
        resume1.addCompany("腾讯");
        resume1.addCompany("阿里巴巴");
        resume1.addCompany("百度");


        Resume resume2= (Resume) resume1.clone();
        resume2.setName("Lucy");
        resume2.setBirthday("1992-05-17");
        resume2.setSchool("华东师范大学");

        resume2.addCompany("小米");


        System.out.println("-------------------- resume1 ----------------------");
        resume1.display();
        System.out.println("-------------------- resume2 ----------------------");
        resume2.display();

我们在resume2里添加了小米,如果是深拷贝,resume1里应该不会有小米。
运行代码结果如下:



从结果可以看到,resume1里出现了小米,说明修改了拷贝,原数据也修改了,对于List company,拷贝数据只是原始数据的引用。

深拷贝
修改clone()代码如下,对浅拷贝的ArrayList company也调用clone()方法。

 /**
     * 克隆该实例
     * @return
     */
    @Override
    protected Object clone(){
        Resume resume=null;

        try {
            resume= (Resume) super.clone();
            // 对List company对象也调用clone()
            resume.company=(ArrayList)this.company.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return resume;
    }

运行结果如下:



可以看到,resume1中已经没有了小米。
原型模式比较简单,比直接new一个对象要方便多了,特别是要在一个循环体中产生大量对象时。
但是,一定要注意,在拷贝过程中,构造方法是不会执行的。

你可能感兴趣的:(原型模式)