博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

设计模式学习--原型模式

Posted on 2017-10-12 17:29  南国木棉  阅读(116)  评论(0编辑  收藏  举报

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

原型模式结构图:

 

原型模式涉及深克隆和浅克隆。

案例需求:制作一份简历,并复制三份。

第一次克隆实现:

1:创建简历类。

package PrototypeModel;

/**
 * 简历类
 * @author 我不是张英俊
 *
 */
public class Resume implements Cloneable {

    private String name;
    private String sex;
    private String age;
    private String timeArea;
    private String company;
    
    public Resume(){
        
    }
    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+"    "+sex+"    "+age);
        System.out.println("工作经历:"+timeArea+"    "+company);
    }
    
    public Object Copy() throws CloneNotSupportedException {
        return this;
    }    
    
    public Object clone() throws CloneNotSupportedException{
        //调用父类Object中的clone方法实现浅克隆
        return super.clone();
    }
}

2:创建测试类

package PrototypeModel;

/**
 * 简历复印,打印一份简历,并复印三份;
 * 原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
 * @author 我不是张英俊
 *
 */
public class Test {

    public static void main(String[] args) throws CloneNotSupportedException {

        Resume a=new Resume("旺财");
        a.SetPersonalInfo("男", "19");
        a.SetWorkExperience("1998-2-3", "百度");
        
        /**
         * Cpoy 方法中的是return this,
         * 返回的是索引,即返回的是指向堆中a对象的索引,
         * 所以,不论怎么重新改变属性,最终改变的都是a对象中的属性,
         * 而此时,并没有在堆中重新copy出两个新的对象b和c。
         * 所以,应该用clone方法来实行,
         * 继承Cloneable接口,并且重写Object中的clone方法。
         * Cloneable接口是一个空的接口,它只是表明,可以重写clone方法,若不实现此接口,不可以重写clone方法
         * 此处使用的是浅克隆。
         */
        Resume b= (Resume) a.Copy();
        Resume b1=(Resume) a.clone();
        b.SetWorkExperience("2019-02-06", "360");
        
        Resume c=(Resume) a.Copy();
        Resume c1=(Resume) a.clone();
        a.SetWorkExperience("2016-03-02", "阿里");
        
        System.out.println("a=  "+a);
        System.out.println("b=  "+b);
        System.out.println("c=  "+c);
        System.out.println("b1= "+b1);
        System.out.println("c1= "+c1);
        /**
         * 控制台输出发现克隆的索引不同,即创建出了新的对象。
         */
        a.Display();
        b.Display();
        c.Display();
        
        a.Display();
        b1.Display();
        c1.Display();
    }

}

3:控制台

a=  PrototypeModel.Resume@6a5c2445
b=  PrototypeModel.Resume@6a5c2445
c=  PrototypeModel.Resume@6a5c2445
b1= PrototypeModel.Resume@47516490
c1= PrototypeModel.Resume@30a14e84
旺财    男    19
工作经历:2016-03-02    阿里
旺财    男    19
工作经历:2016-03-02    阿里
旺财    男    19
工作经历:2016-03-02    阿里
旺财    男    19
工作经历:2016-03-02    阿里
旺财    男    19
工作经历:1998-2-3    百度
旺财    男    19
工作经历:2019-02-06    360

a,b,c三个的地址相同,说明return this返回当前对象的时候返回的是索引,他们指向同一个对象。

而b1,c1地址不同,说明是利用clone方法创建了新的对象。

super.clone()返回的是Object类型的。利用Object类中的clone方法来进行浅克隆。重写Object类中的clone方法需要实现Cloneable接口。

其中Cloneable接口是一个空接口,它只是表明你可以重写clone方法,不实现重写的话,会抛出异常。

第二次克隆实现

事实上在实际开发过程中,工作经历通常会做成工作经历类,这样,就需要深克隆。

先看如果使用浅克隆导致的问题:

1:创建工作经历类。

package PrototypeModel2;

public class workExperence {

    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;
    }
}

2:创建简历类。

package PrototypeModel2;

public class Resume implements Cloneable {

    private String name;
    private String sex;
    private String age;
    private workExperence work;
     
    public Resume(String name){
        this.name=name;
        work=new workExperence();
    }
    
    //设置个人信息
    public void setPersonalInfo(String sex,String age){
        this.sex=sex;
        this.age=age;
    }
    
    //设置工作经历
    public void setWorkExperience(String workDate,String company){
        work.setCompany(company);
        work.setWorkDate(workDate);
    }
    //显示
    public void display(){
        System.out.println(name+"    "+sex+"    "+age);
        System.out.println(work.getCompany()+"      "+work.getWorkDate());
    }
    
    public Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
}

3:测试类。

package PrototypeModel2;

/**
 * 浅克隆会导致的问题
 * 制作简历
 * 实际开发中,会将工作经历作为一个类,
 * @author 我不是张英俊
 *
 */
public class Test {

    public static void main(String arg[]) throws CloneNotSupportedException{
        Resume a=new Resume("旺财");
        a.setPersonalInfo("男", "26");
        a.setWorkExperience("2016-1017", "阿里");
        
        Resume b=(Resume) a.clone();
        b.setWorkExperience("2017-2018", "360");
        
        Resume c=(Resume) a.clone();
        a.setWorkExperience("2018-2019", "腾讯");
        
        a.display();
        b.display();
        c.display();
    }
}

4:控制台。

旺财    男    26
腾讯      2018-2019
旺财    男    26
腾讯      2018-2019
旺财    男    26
腾讯      2018-2019

原因:堆内存中只存在一个workExperience类,所以在修改的时候,总是修改同一个类中的数据,虽然克隆了不同的Resume对象,但不同的Resume对象确是调用同一个workExperience中的数据,所以最后输出的都是最后修改的。

因为clone()方法,对于引用类型,克隆的是其引用,所以,当改变值得时候,就会出现相同的结果。因为三个引用都指向了同一个对象,即唯一的workExperience对象。

如果想显示不同的,就必须对workExperience进行克隆,这样每一个引用不同的workExperience就不会出现这种问题。

第三次克隆实现:

1:创建工作经历类

package PrototypeModel1;
/**
 * 工作经历类
 * @author 我不是张英俊
 *
 */
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;
    }
    
    public Object clone() throws CloneNotSupportedException{
        return super.clone();
    }
}

2:创建一个简历类。

package PrototypeModel1;

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();
    }
    /*
     *提供clone方法调用的私有构造函数,以便克隆“工作经历”的数据 
     */
    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.setCompany(company);
        work.setWorkDate(workDate);
    }
    
    //展示
    public void display(){
        System.out.println(name+"    "+sex+"    "+age);
        System.out.println("工作经历    "+work.getCompany()+"      "+work.getWorkDate());
    }
    
    public Object clone() throws CloneNotSupportedException{
        /**
         * 调用私有的构造方法,让“工作经历”克隆完成,然后再给这个“简历”
         * 对象相关字段赋值,最终返回一个深克隆的简历对象。
         */
        Resume obj=new Resume(this.work);
        obj.age=this.age;
        obj.name=this.name;
        obj.sex=this.sex;
        return obj;
    }
}

3:创建一个测试类。

package PrototypeModel1;

/**
 * 简历复印,
 * 原型模式,
 * 深拷贝和浅拷贝
 * @author 我不是张英俊
 *
 */
public class Test {

    public static void main(String[] args) throws CloneNotSupportedException {
        Resume a =new Resume("旺财");
        a.setPersonalInfo("男", "19");
        a.setWorkExperience("2019-2020", "阿里");
        
        Resume b=(Resume)a.clone();
        b.setWorkExperience("2017-2018", "腾讯");
        
        Resume c=(Resume)a.clone();
        c.setWorkExperience("2016-2017", "华为");
        
        a.display();
        b.display();
        c.display();
    }

}

4:控制台。

旺财 男 19
工作经历 阿里 2019-2020
旺财 男 19
工作经历 腾讯 2017-2018
旺财 男 19
工作经历 华为 2016-2017

利用深克隆解决问题。创建了堆内存中实际存在了三个workExperience对象。

总结:理解尚浅,暂时未想到。