-
原型模式中利用一个原型对象来指明我们需要创建对象的类型,然后通过赋值这个对象来获取一个与该对象一模一样的对象实例。
-
原型模式就是用原型实例指定创建对象的种类,并且通过克隆这些原型来创建新的对象。
-
在原型模式中,创建对象是通过请求原型对象类型来拷贝原型对象自己来创建对象,也就是只需要知道原型对象的类型就可以获得更多的原型实例对象。
-
拷贝的类型
- 浅拷贝:使用一个已知实例对新创建的实例的成员变量逐个赋值,其实是对值得地址引用。
- 深拷贝:当一个类的拷贝构造方法,不仅要复制对象的所有非成员变量值,还要为引用类型的成员变量创建新的实例,并且初始化为形参实例值(真的复制了成员变量)。
-
原型模式的UML图
-
原型模式的代码实现
-
创建一个需要克隆的对象,需要实现Serializable接口
import java.io.*; import java.util.Date; import java.util.List; public class Student implements Serializable { private String name; private Date birthDay; private School school; private List<String> friends; public String getName() { return name; } public void setName(String name) { this.name = name; } public Date getBirthDay() { return birthDay; } public void setBirthDay(Date birthDay) { this.birthDay = birthDay; } public School getSchool() { return school; } public void setSchool(School school) { this.school = school; } public List<String> getFriends() { return friends; } public void setFriends(List<String> friends) { this.friends = friends; } /** * 核心方法,用于克隆Student对象 * @return * @throws CloneNotSupportedException */ public Student deepClone() throws IOException, ClassNotFoundException { //第一版,浅克隆,需要实现Cloneable接口 //Student student = (Student)super.clone(); //第二版,普通克隆,需要实现Cloneable接口,但是无法克隆JDK类中没有实现Cloneable接口的类 /*Student student = (Student)super.clone(); student.school = (School) school.clone(); */ //第三版,深度克隆,不需要实现Cloneable接口,关联的对象需要实现Serializable接口, // 深度克隆是基于序列化接口的,序列化相关的类,是对流的复制操作,与Cloneable无关 //将对象写入到流里 ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(baos); oos.writeObject(this); //从流里将对象读出 ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bais); Student student = (Student)ois.readObject(); return student; } }
-
创建一个克隆对象引用的对象,需要实现Serializable接口
import java.io.Serializable; public class School implements Serializable { private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } }
-
编写克隆的测试类
import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.List; public class Test { public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException { Student student1 = new Student(); student1.setName("张三"); student1.setBirthDay(new Date()); List<String> friends = new ArrayList<>(); friends.add("zhangsan"); friends.add("lisi"); student1.setFriends(friends); School school = new School(); school.setName("剑桥"); student1.setSchool(school); Student student2 = student1.deepClone(); student2.setName("李四"); student2.getSchool().setName("清华"); student2.getFriends().add("王五"); System.out.println("student1.name: "+student1.getName()); System.out.println("student2.name: "+student2.getName()); System.out.println("student1.birthday: "+student1.getBirthDay()); System.out.println("student2.birthday: "+student2.getBirthDay()); System.out.println("student1.friends: "+student1.getFriends()+student1.getFriends().size()); System.out.println("student2.friends: "+student2.getFriends()+student1.getFriends().size()); System.out.println("student1.school: "+student1.getSchool().getName()); System.out.println("student2.school: "+student2.getSchool().getName()); } } /** 执行结果为: student1.name: 张三 student2.name: 李四 student1.birthday: Sun Dec 08 15:45:29 CST 2019 student2.birthday: Sun Dec 08 15:45:29 CST 2019 student1.friends: [zhangsan, lisi]2 student2.friends: [zhangsan, lisi, 王五]2 student1.school: 剑桥 student2.school: 清华 */
-
-
原型模式的优点
- 如果创建新的对象比较复杂时,可以利用原型模式简化对对象的创建过程,同时也能够提高效率。
- 可以使用深度克隆保持对象的状态
- 原型模式提供了简化的创建结构
-
原型模式的缺点
- 在实现深度克隆的时候可能需要比较复杂的代码
- 需要为每一个类配备一个克隆方法,而且这个克隆方法需要对类的功能进行通盘考虑,这对全新的类来说不是很难,但对已有的类进行改造时,必须要修改其源代码,违背了"开闭原则"
-
使用场景
- 创建新对象成本较高,可以利用已有的对象进行克隆
- 如果系统要保持对象状态,且对象变化小,或者对象本身占用内存不大时,可以使用原型模式或者备忘录模式来使用;相反,如果对象的变化较大或者占用的内存很大,建议使用原型模式
- 需要避免使用分层次的工厂类来创建分层次的对象(一个对象中引用了别的对象),并且类的实例对象只有一个或者很少的几个组合状态,通过复制原型对象得到新实例比构造函数创建一个新实例更加方便