首页 > 技术文章 > 设计模式-原型模式

leisurexi 2019-03-10 13:55 原文

    本篇文章主要讲解原型模式。

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

    先看下结构图。

    原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。接下来我们看看基本代码。

    在JAVA语言中使用原型模式是非常简单的,这是因为Object类当中提供了一个本地方法clone,而JAVA的任何类只要实现了Cloneable接口,就可以使用clone方法来进行对象的拷贝。

    代码我就没按照结构图上的写了,就随便写个简单的示例。

package com.lwx.prototype;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: lwx
 * Date: 2019-03-10
 * Time: 11:51
 */
public class Student implements Cloneable {

    private String name;
    private int age;

    public Student() {
        System.out.println("student类构造方法执行");
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    protected Student clone()  {
        Student student = null;
        try {
            student = (Student) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return student;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

    接下看下测试类和运行结果。

package com.lwx.prototype;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: lwx
 * Date: 2019-03-10
 * Time: 11:57
 */
public class Test {

    public static void main(String[] args) {
        Student student1 = new Student();
        student1.setName("小明");
        student1.setAge(18);
        System.out.println(student1.toString());

        Student student2 = student1.clone();
        System.out.println(student2.toString());
    }

}

     从输出结果可以看出,clone方法将student1复制了一个,然后赋给了student2,这就像复制粘贴一样,值得注意的是,在使用Object.clone()方法去拷贝一个对象时,构造方法是不会执行的,否则应该输出俩次 "student类构造方法执行"。

    从原型模式的使用可以看出原型模式常用于以下场景:

    1.一般在初始化信息不发生变化的情况下,克隆是最好的办法。

    2.在运行过程中不知道对象集体类型,可使用原型模式创建一个相同类型的对象。

    Object.clone()方法执行的是浅拷贝,被复制对象的所有变量都含有与原来的对象相同的值,而所有其他对象的引用都仍然指向原来的对象。我们把上面的Student类改进一下加入对其他对象引用。

    

    我们先建一个School类,然后在Student类中引用它。

package com.lwx.prototype;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: lwx
 * Date: 2019-03-10
 * Time: 12:00
 */
public class School {

    private String name;
    private String address;

    public String getName() {
        return name;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

}

    改过后Student类。

package com.lwx.prototype;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: lwx
 * Date: 2019-03-10
 * Time: 11:51
 */
public class Student implements Cloneable {

    private String name;
    private int age;

    private School school;

    public Student() {
        this.school = new School();
        school.setName("xx学校");
        school.setAddress("xx市xx路");
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public School getSchool() {
        return school;
    }

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

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }
}

     输出结果。

    可以看出school引用的同一对象的地址,这种情况下修改student1里school的熟悉name值,student2的也会跟着改变。

     然而要实现深度拷贝,需要将实现了Cloneable接口并重写了clone方法的类中,所有的引用类型也全部实现Cloneable接口并重写clone方法,而且需要将引用类型的属性全部拷贝一遍。

    下面将上面例子改成深度拷贝。

    首先让school类实现Cloneable接口并重写clone方法。

package com.lwx.prototype;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: lwx
 * Date: 2019-03-10
 * Time: 12:00
 */
public class School implements Cloneable{

    private String name;
    private String address;

    public String getName() {
        return name;
    }

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

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    @Override
    protected School clone() {
        School school = null;
        try {
            school = (School) super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return school;
    }
}

    然后修改下Student类中的clone方法。

package com.lwx.prototype;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: lwx
 * Date: 2019-03-10
 * Time: 11:51
 */
public class Student implements Cloneable {

    private String name;
    private int age;

    private School school;

    public Student() {
        this.school = new School();
        school.setName("xx学校");
        school.setAddress("xx市xx路");
    }

    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public School getSchool() {
        return school;
    }

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

    @Override
    protected Student clone()  {
        Student student = null;
        try {
            student = (Student) super.clone();
            student.school = school.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return student;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", school=" + school +
                '}';
    }
}

    输出结果。

    

    原型模式的优缺点:

    优点:

      1.由于clone方法是有虚拟机直接复制内存块执行,所以在速度上比使用new的方式创建对象要快。

      2.可以基于原型,快速的创建一个对象,而无需知道创建的细节。  

    缺点:

      1.实现深度拷贝比较困难,需要很多额外的代码。

      2.多层深度拷贝时要小心循环引用。

 

    最后附上demo的githup地址:https://github.com/yijinqincai/design_patterns

推荐阅读