设计模式之原型模式

设计模式系列专题(更新中)

设计模式之策略模式java

设计模式之代理模式设计模式

设计模式之专题模式bash

设计模式之建造者模式markdown

设计模式之装饰模式ide

设计模式之观察者模式函数

设计模式之责任链模式post

设计模式之命令模式测试

设计模式之门面模式this

1、定义

       用原型实例指定建立对象的种类,而且经过拷贝这些原型建立新的对象。(来源:百度百科)spa

        原型模式能够说是最简单的设计模式之一了,从定义就能够知道它的所有内容。经过从“原型实例”中拷贝出新的对象。拷贝出新的对象,因此也能够认为原型模式就是建立对象的另外一种方式。 Java中Object已经为咱们提供了clone方法,此方法是一个native方法(C、或C++实现的),用来帮咱们拷贝对象,不过这种拷贝是浅拷贝。

2、拷贝

浅拷贝

        被复制对象(一个新的对象实例)的全部变量都含有与原来的对象相同的值,对于基本数据类型的属性复制一份给新产生的对象,对于非基本数据类型的属性仅仅复制一份引用给新产生的对象。即对于对象类型的实例变量,新拷贝出来的对象里面的变量指向的对象与原来对象是同一个对象。

深拷贝

        被复制对象(一个新的对象实例)的全部变量都含有与原来的对象相同的值,而那些引用其余对象的变量将指向被复制过的新对象(新内存空间),而再也不是原有的那些被引用的对象。

        从这两个定义不知你是否知道了浅拷贝,深拷贝的区别,文字说明可能不太好理解,接下来看个小例子,可能就能一目了然。

package com.prototype;
/**
*父亲类
*/
public class Father {
    private String name;
    private int age;
    //省略get、set方法
}

/**
*学生类,java中实现拷贝的类必须实现Cloneable接口,其实这只是一个标记接口,
*接口并无实际内容,不然调Object的clone方法会抛出异常
*/
public class Student implements Cloneable{

    private int stuNum;
    private String name;

    private Father father;

    public Student Clone(){
        Student stu = null;
        try {
            //调用Object提供的clone方法来拷贝
            stu = (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return  stu;
    }

    public  void showStuMsg(){
        System.out.println("学生名称:"+name+",学号:"+stuNum+",父亲名称:"+ father.getName()+",父亲年龄:"+ father.getAge());
    }
    //省略get、set方法
}

/**
测试代码
*/
package com.prototype;

public class TestMain {

    public static void main(String[] args) {

        //建立学生zhangsan
        Student zhangsan = new Student();
        zhangsan.setName("张三");
        zhangsan.setStuNum(14);
        Father fatherzhangsan = new Father();
        fatherzhangsan.setName("张三的老爸");        
        fatherzhangsan.setAge(47);        
        zhangsan.setFather(fatherzhangsan);
        //从zhangsan中拷贝出李四
        Student lisi = zhangsan.Clone();
        //设置李四名字,学号
        lisi.setName("李四");
        lisi.setStuNum(15);
        //改变李四的父亲名称,年龄
        lisi.getFather().setName("李四的老爸");
        lisi.getFather().setAge(48);
        //展现学生信息
        zhangsan.showStuMsg();
        lisi.showStuMsg();
    }
}
复制代码

执行结果


能够看到,咱们改了李四的父亲的名称,年龄,结果张三的父亲也跟着改变了,这实际上是由于张3、李四指向同一个父亲对象了。而改变学号,并无同步改变了张三的学号。这就是浅拷贝,对基本类型变量会建立新变量;对对象类型变量,变量同被拷贝者指向同样的对象。

深拷贝

那么怎么实现深拷贝呢?有两种方式:

方式一

//让父亲类提供克隆方法
package com.prototype;

public class Father implements Cloneable{
    private String name;
    private int age;
    //省略get、set方法
    public Father clone(){
        Father father = null;
        try {
            father = (Father) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return father;
    }
}
//学生类clone时,克隆父亲类
package com.prototype;

public class Student implements Cloneable{
    private int stuNum;
    private String name;
    private Father father;

    public Student Clone(){
        Student stu = null;
        try {
            stu = (Student) super.clone();
            //克隆父类,并设置到拷贝出来的学生类里
            stu.setFather(father.clone());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return  stu;
    }

    //省略get、set方法
    public  void showStuMsg(){
        System.out.println("学生名称:"+name+",学号:"+stuNum+",父亲名称:"+ father.getName()+",父亲年龄:"+ father.getAge());
    }
}
//测试代码同上...复制代码

执行结果,能够看到张3、李四各有本身父亲了。


方式二

不须要实现Cloneable接口了,父亲类和学生类都实现Serializable,经过对象流来拷贝

//父类
package com.prototype;

import java.io.Serializable;

public class Father implements Serializable{
    private String name;
    private int age;
    //省略get、set方法
}

//学生类
package com.prototype;

import java.io.*;

public class Student implements Serializable{

    private int stuNum;
    private String name;
    private Father father;
    //经过Object流来拷贝
    public Student deepClone(){
        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        ObjectOutputStream oo = null;
        try {
            oo = new ObjectOutputStream(bo);
            //将当前对象写入到流里面
            oo.writeObject(this);
            ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
            ObjectInputStream oi = new ObjectInputStream(bi);
            //从流里面对出对象,即位拷贝出来的对象
            return (Student) oi.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        }catch (ClassNotFoundException e){
            e.printStackTrace();
        }
        return null;
    }
    //省略get、set方法
    public  void showStuMsg(){
        System.out.println("学生名称:"+name+",学号:"+stuNum+",父亲名称:"+ father.getName()+",父亲年龄:"+ father.getAge());
    }
}
//测试代码同上,只修改调用的拷贝方法名

Student lisi = zhangsan.deepClone();复制代码

执行结果同方式一。

3、UML图

  照例,咱们依然经过例子来了解这个“原型模式”这个设计模式。场景:咱们有一份试卷的原型,但愿经过这份试卷拷贝出多份,供考试使用。看看原型模式的UML图

Prototype抽象类(原型抽象类)定义了一个clone方法。

TestPaper具体的原型类,实现clone方法。


4、代码实现

看过了上面的浅、深拷贝,代码实现也很简单了,只需按照UML图编写对应的几个类便可。这里咱们增长一个阅读题类,经过深拷贝来复制试卷。

//原型抽象类
package com.prototype;

public abstract class Prototype {
    public abstract Prototype Clone();
}

//阅读题类************************************************************************
package com.prototype;
/**
 * 阅读题
 */
public class ReadingTopics extends Prototype implements Cloneable{
    private String topicTitle;
    private String topicContent;

    public ReadingTopics(String topicTitle, String topicContent) {
        this.topicTitle = topicTitle;
        this.topicContent = topicContent;
    }
    //省略get、set方法
    @Override
    public Prototype Clone() {
        ReadingTopics readingTopics = null;
        try {
            readingTopics = (ReadingTopics) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return readingTopics;
    }

}

//试卷类*************************************************************************
package com.prototype;

public class TestPager extends Prototype implements Cloneable{

    private String paperTitle;//试卷标题
    private ReadingTopics readingTopics;//阅读理解

    public  void showPagerContent(){
        System.out.println("*******试卷内容*******");
        System.out.println("试卷:"+paperTitle);
        System.out.println("阅读题:"+readingTopics.getTopicTitle()+","+readingTopics.getTopicContent());
        System.out.println("********结束*********");
    }
    //省略get、set方法
    @Override
    public Prototype Clone() {
        TestPager testPager = null;
        try {
            testPager = (TestPager) super.clone();
            testPager.setReadingTopics((ReadingTopics) readingTopics.Clone());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return testPager;
    }
}
//测试类**************************************************************************
package com.prototype;

public class TestMain {

    public static void main(String[] args) {
        TestPager testPager = new TestPager();
        testPager.setPaperTitle("高二语文试卷");
        testPager.setReadingTopics(new ReadingTopics("水浒传选读","武松打虎片断!!"));

        TestPager cloneObj = (TestPager) testPager.Clone();
        cloneObj.getReadingTopics().setTopicTitle("三过演义");
        cloneObj.getReadingTopics().setTopicContent("草船借箭片断!!");
        testPager.showPagerContent();
        cloneObj.showPagerContent();

    }
}
复制代码

执行结果,能够看到咱们成功拷贝出一份新的试卷。


5、结论总结

很明显,原型模式就是从已有对象拷贝出一个状态与已有对象一摸同样的新对象。主要的应用场景:构造函数执行时间很长,执行效率低,便可采用拷贝(克隆,并不走构造方法)。

参考:《大话设计模式》

相关文章
相关标签/搜索