设计模式-原型模式
本篇文章主要讲解原型模式。
原型模式:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建的新对象。
先看下结构图。
原型模式其实就是从一个对象再创建另外一个可定制的对象,而且不需知道任何创建的细节。接下来我们看看基本代码。
在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