大话设计模式读书笔记(原型模式)

人物:小菜,大鸟

事件:小菜正在准备求职见面会,打印了很多简历,大鸟让小菜试着用程序写简历


了解原型模型:

1.通过用原始的逻辑实现几份相同简历的打印

2.思考有没有一种模型可以帮助小菜不用新建对象,直接复制简历 --引出原型模型

3.通过试验值复制和引用复制,知道了深复制和浅复制,然后引出深复制的实现原理和实现内容

小菜简历代码初步实现

简历类:

@Slf4j
public class Resume {
    private String name;
    private String sex;
    private String age;
    private String timeArea;
    private String company;

    public Resume(String name) {
        this.name = name;
    }

    /**
     * 设置个人信息
     *
     * @param sex
     * @param age
     */
    public void setPersonalInfo(String sex, String age) {
        this.sex = sex;
        this.age = age;
    }

    /**
     * 设置工作经历
     *
     * @param timeArea
     * @param company
     */
    public void setWorkExperience(String timeArea, String company) {
        this.timeArea = timeArea;
        this.company = company;
    }

    /**
     * 显示
     */
    public void display() {
        log.info("名字:{} 性别:{} 年龄:{}", name, sex, age);
        log.info("工作经历:{}, {}", timeArea, company);
    }
}

打印三份,客户端调用代码:

public class ResumeDisplay {
    public static void main(String[] args) {
        Resume a = new Resume("大鸟");
        Resume b = new Resume("大鸟");
        Resume c = new Resume("大鸟");
        a.setPersonalInfo("男", "29");
        b.setPersonalInfo("男", "29");
        c.setPersonalInfo("男", "29");
        a.setWorkExperience("2019-2020", "凡人修仙公司");
        b.setWorkExperience("2019-2020", "凡人修仙公司");
        c.setWorkExperience("2019-2020", "凡人修仙公司");
        a.display();
        b.display();
        c.display();
    }
}

大鸟:这样如果要打印20份,也就要实例20个对象,如果写好20份后,如果要改下年份,那不是20个要一个一个改么?你其实可以先这样改下:

public class ResumeDisplay {
    public static void main(String[] args) {
        Resume a = new Resume("大鸟");
        a.setPersonalInfo("男", "29");
        a.setWorkExperience("2018-2020", "凡人修仙公司");
        Resume b = a;
        Resume c = a;
        a.display();
        b.display();
        c.display();
    }
}

大鸟:这样就比刚才好多了,其实是传引用,不是传值,就像一份简历,每次复制与内容有关,与用什么纸无关,下面我们就可以开始了解原型模型了

简历的原型模型实现

原型模型:用原型模型指定创建对象的种类,并通过拷贝这些原型创建新的对象(而且不用知道创建的细节)

简历类:

@Slf4j
public class Resume implements Cloneable {
    private String name;
    private String sex;
    private String age;
    private String timeArea;
    private String company;

    public Resume(String name) {
        this.name = name;
    }

    /**
     * 设置个人信息
     *
     * @param sex
     * @param age
     */
    public void setPersonalInfo(String sex, String age) {
        this.sex = sex;
        this.age = age;
    }

    /**
     * 设置工作经历
     *
     * @param timeArea
     * @param company
     */
    public void setWorkExperience(String timeArea, String company) {
        this.timeArea = timeArea;
        this.company = company;
    }

    /**
     * 显示
     */
    public void display() {
        log.info("名字:{} 性别:{} 年龄:{}", name, sex, age);
        log.info("工作经历:{}, {}", timeArea, company);
    }

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

客户端调用:

@Slf4j
public class ResumeDisplay {
    public static void main(String[] args) {
        Resume a = new Resume("大鸟");
        a.setPersonalInfo("男", "29");
        a.setWorkExperience("2019-2020", "凡人修仙功法公司");

        Resume b = (Resume) a.clone();
        b.setWorkExperience("2020-2021", "凡人修仙宝器公司");

        Resume c = (Resume) a.clone();
        c.setWorkExperience("2020-2021", "凡人修仙炼体公司");

        a.display();
        b.display();
        c.display();
    }
}

大鸟:对的,这样实现后,这样每次复制简历或者要修改简历,都是直接克隆,而不是再去创建一个新的实例,大大提高了性能,还隐藏了实现的细节

大鸟:但注意了,我们刚才复制的,都是String类型,也就是都是值类型,但如果是引用类型,就只会复制引用,不会复制引用的类型,因此原始对象及其复本引用同一对象

如下:

@Slf4j
@Data
public class Resume implements Cloneable {
    private String name;
    private String sex;
    private String age;
    private String timeArea;
    private String company;
    private WorkExperience work;

    public Resume(String name) {
        this.name = name;
        work = new WorkExperience();
    }

    /**
     * 设置个人信息
     *
     * @param sex
     * @param age
     */
    public void setPersonalInfo(String sex, String age) {
        this.sex = sex;
        this.age = age;
    }

    /**
     * 设置工作经历
     *
     * @param timeArea
     * @param company
     */
    public void setWorkExperience(String timeArea, String company) {
        work.setTimeArea(timeArea);
        work.setCompany(company);
    }

    /**
     * 显示
     */
    public void display() {
        log.info("名字:{} 性别:{} 年龄:{}", name, sex, age);
        log.info("工作经历:{}, {}", work.getTimeArea(), work.getCompany());
    }

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

其中:private WorkExperience work是引用工作类型的对象

在简历实例化时,同时实例化工作经历:

public Resume(String name) {
    this.name = name;
    work = new WorkExperience();
}

在设置工作经历时,给对象的两属性值赋值:

/**
 * 设置工作经历
 *
 * @param timeArea
 * @param company
 */
public void setWorkExperience(String timeArea, String company) {
    work.setTimeArea(timeArea);
    work.setCompany(company);
}

这时如果再执行之前的客户端代码,则都只显示a对象的信息

大鸟小结:如果是值对象,直接复制没有问题,如果是引用类型,只是复制了引用,对引用的对象还是指向了原来的对象,这叫做浅复制。浅复制指被复制对象的所有变量都含有与原来对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。当我们需要把所有引用的对象都复制一遍时,这就需要深复制,深复制会把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象

深复制:

现在工作经历类中,复制传进来的值:

@Data
public class WorkExperience implements Cloneable {
    private String timeArea;
    private String company;

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

然后在简历类中,先将传进来的工作经历复制,将a里工作经历的内容重新赋值后传了一个新对象给b,b也就得到了改变的值:

@Slf4j
@Data
public class Resume implements Cloneable {
    private String name;
    private String sex;
    private String age;
    private String timeArea;
    private String company;
    private WorkExperience work;

    public Resume(String name) {
        this.name = name;
        work = new WorkExperience();
    }

    /**
     * 设置个人信息
     *
     * @param sex
     * @param age
     */
    public void setPersonalInfo(String sex, String age) {
        this.sex = sex;
        this.age = age;
    }

    /**
     * 设置工作经历
     *
     * @param timeArea
     * @param company
     */
    public void setWorkExperience(String timeArea, String company) {
        work.setTimeArea(timeArea);
        work.setCompany(company);
    }

    /**
     * 显示
     */
    public void display() {
        log.info("名字:{} 性别:{} 年龄:{}", name, sex, age);
        log.info("工作经历:{}, {}", work.getTimeArea(), work.getCompany());
    }

    @Override
    public Object clone() {
        Resume obj = new Resume(this.work);
        obj.name = this.name;
        obj.sex = this.sex;
        obj.age = this.age;
        return obj;
    }

    private Resume(WorkExperience work) {
        try {
            this.work = (WorkExperience)work.cloneInfo();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

这样得到的结果就不像浅复制一样都是一样的了,而是:

名字:大鸟 性别:男 年龄:29
工作经历:2019-2020, 凡人修仙功法公司
名字:大鸟 性别:男 年龄:29
工作经历:2020-2021, 凡人修仙宝器公司
名字:大鸟 性别:男 年龄:29
工作经历:2020-2021, 凡人修仙炼体公司
名字:大鸟 性别:男 年龄:29
原文地址:https://www.cnblogs.com/wencheng9012/p/13392730.html