菜鸟之路:23种设计模式之——原型模式

什么是原型模式?

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

原型模式其实就是一个对象在创建另一个可定制的对象,而且不需要指定任何创建的细节。Java提供了Coneable接口,其中有一个唯一方法Clone(),实现这个接口就可以完成原型模式了。

什么时候使用它呢?

一般在初始化的信息不发生变化的情况下,克隆是最好的办法。既隐藏了对象创建的细节,又对性能是大大的提高。不用重新初始化对象,而是动态地获得对象运行时的状态。

 

原型模式的结构图:

 

面试的时候需要递交简历,很多家公司需要去面试,一份简历就需要复印很多份,下面通过简历的事件来模拟原型模式。

原型模式实现:

简历类

/**
 * 作者:LKP
 * 时间:2018/7/27
 * 简历类
 */
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;
    }

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

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

    //显示
    public void Display(){
        System.out.println(name+"\t"+sex+"\t"+age);
        System.out.println("工作经历:"+timeArea+"\t"+company);
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

客户端实现:

/**
 * 作者:LKP
 * 时间:2018/7/27
 * 客户端
 */
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Resume a = new Resume("小明");
        a.SetPersonalInfo("男","29");
        a.SetWorkExperience("1998-2000","xx公司");

        Resume b = (Resume) a.clone();
        b.SetWorkExperience("1998-2018","YY公司");

        Resume c = (Resume) a.clone();
        c.SetPersonalInfo("男","24");

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

运行结果

只需要调用Clone方法就可以实现新简历的生成,并且可以再修改新简历的细节。

它的好处,不用重新初始化对象,而是动态地获得对象运行时的状态。

 

原型模式有两种情况:

一种是浅复制,一种是深复制。

 

Clone()方法是这样的,如果字段是值类型的,则对该字段执行逐位复制,如果字段是引用类型,则复制引用但不复制引用的对象;因此,原始对象及其复本引用同一对象。

接下来我们来看看这一现象:

新增工作经历类:

/**
 * 作者:LKP
 * 时间:2018/7/27
 * 工作经历类
 */
public class WorkExperience {
    private String workDate;
    private String company;

    public String getWorkDate() {
        return workDate;
    }

    public void setWorkDate(String workDate) {
        this.workDate = workDate;
    }

    public String getCompany() {
        return company;
    }

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

修改简历类:

引用“工作经历”对象;在“简历”类实例化时同时实例化“工作经历”。

/**
 * 作者:LKP
 * 时间:2018/7/27
 * 简历类
 */
public class Resume implements Cloneable {
    private String name;
    private String sex;
    private String age;

    private WorkExperience work;

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

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

    //设置工作经历
    public void SetWorkExperience(String workDate,String company){
        work.setWorkDate(workDate);
        work.setCompany(company);
    }

    //显示
    public void Display(){
        System.out.println(name+"\t"+sex+"\t"+age);
        System.out.println("工作经历:"+work.getWorkDate()+"\t"+work.getCompany());
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

客户端代码进行细微修改:

/**
 * 作者:LKP
 * 时间:2018/7/27
 * 客户端
 */
public class Client {
    public static void main(String[] args) throws CloneNotSupportedException {
        Resume a = new Resume("小明");
        a.SetPersonalInfo("男","29");
        a.SetWorkExperience("1998-2000","xx公司");

        Resume b = (Resume) a.clone();
        b.SetWorkExperience("1998-2018","YY公司");

        Resume c = (Resume) a.clone();
        a.SetWorkExperience("1998-2000","ZZ公司");

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

b和c都克隆于a,但当它们都设置了“工作经历”时,我们希望的结果是三个的显示不一样。

运行结果:

可惜,没有达到我们的要求,三次显示的结果都是最后一次设置的值。

 

这种原因,就叫做”浅复制“,被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。

我们可能更需要这样一个需求:把要复制的对象所引用的对象都复制一遍

比如刚刚的例子,我们希望是a、b、c三个引用的对象都是不同的,复制时就一变二,二变三。这种复制的方式叫做“深复制”,深复制把引用对象的变量指向复制过的新对象,而不是原有的被引用的对象。

 

深复制代码实现:

修改我们刚刚创建的工作经历类,让其实现Coneable。“工作经历”类实现克隆方法。

/**
 * 作者:LKP
 * 时间:2018/7/27
 * 工作经历类
 */
public class WorkExperience implements Cloneable{
    private String workDate;
    private String company;

    public String getWorkDate() {
        return workDate;
    }

    public void setWorkDate(String workDate) {
        this.workDate = workDate;
    }

    public String getCompany() {
        return company;
    }

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

简历类修改如下:

/**
 * 作者:LKP
 * 时间:2018/7/27
 * 简历类
 */
public class Resume implements Cloneable {
    private String name;
    private String sex;
    private String age;

    private WorkExperience work;

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

    private Resume(WorkExperience work) throws CloneNotSupportedException {
        this.work = (WorkExperience) work.clone();
    }

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

    //设置工作经历
    public void SetWorkExperience(String workDate,String company){
        work.setWorkDate(workDate);
        work.setCompany(company);
    }

    //显示
    public void Display(){
        System.out.println(name+"\t"+sex+"\t"+age);
        System.out.println("工作经历:"+work.getWorkDate()+"\t"+work.getCompany());
    }

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

提供Clone方法调用私有构造函数,以便克隆“工作经历”的数据。

在clone方法调用私有的够着方法,让“工作经历”克隆完成,然后再给这个“简历”对象的相关字段赋值,最终返回一个深复制的简历对象。

客户端代码不变,运行结果显示:

达到了我们希望三次显示结果各不相同的需求。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

良月柒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值