【设计模式】原来这就是原型模式
原型模式介绍
概述
原型模式顾名思义,就是基于原型来创建对象,用人话说就是一个对象的产生可以不由零起步,直接从一个已经具备一定雏形的对象克隆,然后再修改为所需要的对象。显而易见 ,原型模式属于创建型模式,
使用场景
如果对象的创建成本比较大,例如某个对象里面的数据需要访问数据库才能拿到;并且同一个类的不同对象之间差别不大(大部分字段都相同),这种场景下可以考虑使用原型模式达到提高程序性能的作用。
我们可以通过一个业务场景来理解原型模式的应用,设计一个学生类,学生类主要的成员变量有名字name,班级classId, needExtraCourse,以及所学的课程 course,其中course需要通过rpc调用查询课程系统获取,这个耗时100ms。如果用普通的方式为该类创建同一个班的4个 对象,如下所示:
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
class Student{
private String name;
private Integer classId;
//是否需要额外多选课程
private Boolean needExtraCourse = false;
//所学课程,需要通过rpc调用查询课程系统,耗时100ms
private List<String> course = new ArrayList<>();
public Student(String name, Integer classId) throws InterruptedException {
this.name = name;
this.classId = classId;
//超时模拟rpc调用获取课程信息初始化course
Thread.sleep(100);
course.add("语文");
course.add("数学");
course.add("英语");
}
//getter、setter、toString函数省略
}
public class PrototypeExample {
public static void main(String[] args) throws InterruptedException {
long start = new Date().getTime();
Student student1 = new Student("张三", 1);
Student student2 = new Student("李四", 1);
Student student3 = new Student("王五", 1);
Student student4 = new Student("赵六", 1);
long end = new Date().getTime();
System.out.println("创建对象共花费了时间:" + (end -start) + " ms");
}
}
******************【运行结果】******************
创建对象共花费了时间:413
ms
只是创建了4个对象就花费了400多ms,这样成本也太大了,其实对于一个班级的学生,不考虑额外选修课程的情况下(needExtraCourse = false),所学的课程应该是完全一样的。
因此可以用到今天介绍的原型模式:先用普通的方式创建一个对象,然后从创建的对象中克隆出其它对象,再修改其它对象的name字段即可。
UML类图
UML 类图也比较简单,只有两个部分:
• 1.Cloneable接口
• 2.Student类,实现了Cloneable接口的原型对象,这个对象有个能力就是可以克隆自己。
原型模式实现
原型模式的实现方式有两种:浅拷贝和深拷贝。关于浅拷贝和深拷贝,可以阅读详解浅拷贝与深拷贝
浅拷贝模式
浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。Object类提供的方法clone只是拷贝本对象 , 其对象内部的数组、引用对象等都不拷贝,还是指向原生对象的内部元素地址,下面是浅拷贝原型模式实现的代码:
//1原型类需实现Cloneable接口
class Studentimplements Cloneable
{
//Student类的其余部分和上面例子一样
......................@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class PrototypeExample {
public static void main(String[] args) throws InterruptedException, CloneNotSupportedException {
long start = new Date().getTime();
Student student1 = new Student("张三", 1);
Student student2 = (Student) student1.clone();
student2.setName("李四");
Student student3 = (Student) student1.clone();
student3.setName("王五");
Student student4 = (Student) student1.clone();
student4.setName("赵六 ");
System.out.println("student1--->" + student1);
System.out.println("student2--->" + student2);
System.out.println("student3--->" + student3);
System.out.println("student4--->" + student4);
long end = new Date().getTime();
System.out.println("创建对象共花费了时间:" + (end -start) + " ms");
//赵六选修了课程美术
student4.setNeedExtraCourse(true);
student4.getCourse().add("美术 ");
System.out.println("\n赵六选修美术课后:");
System.out.println("student1--->" + student1);
System.out.println("student2--->" + student2);
System.out.println("student3--->" + student3);
System.out.println("student4--->" + student4);
}
}
******************【运行结果】******************
student1--->Student{name='张三', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
student2--->Student{name='李四', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
student3--->Student{name='王五', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
student4--->Student{name='赵六 ', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
创建对象共花费了时间:130
ms
赵六选修美术课后:
student1--->Student{name='张三', classId=1, needExtraCourse=false, course=[语文, 数学, 英语,美术
]}
student2--->Student{name='李四', classId=1, needExtraCourse=false, course=[语文, 数学, 英语,美术
]}
student3--->Student{name='王五', classId=1, needExtraCourse=false, course=[语文, 数学, 英语,美术
]}
student4--->Student{name='赵六 ', classId=1, needExtraCourse=true, course=[语文, 数学, 英语,美术
]}
可以看到,通过原型模式的方式,同样是创建四个对象,只花了100多ms,大大的提高了程序性能。
但是这里还存在一个小问题:赵六比较好学,所以还选修了美术,但是通过程序的运行结果可以看到,明明只是赵六选修了美术,但是其他三个同学的课表中也都出现了美术课 。这是因为浅拷贝虽然产生了两个完全不同的对象,但是对象中有对其他对象的引用(如这里的List)都指向同一个对象。
为了解决这个问题,我们引入了深拷贝模式。
深拷贝模式
深拷贝模式把要复制的对象所引用的对象都拷贝了一遍。
class Student
implements Cloneable
{
//Student类的其余部分和上面例子一样
......................
@Override
public Object clone() throws CloneNotSupportedException {
Object object = super.clone();
Student student = (Student)object;
List<String> newCourse = new ArrayList<>();
Iterator<String> it = student.course.iterator();
while (it.hasNext()) {
newCourse.add(it.next());
}
student.course = newCourse;
return object;
}
}
public class PrototypeExample {
public static void main(String[] args) throws InterruptedException, CloneNotSupportedException {
long start = new Date().getTime();
Student student1 = new Student("张三", 1);
Student student2 = (Student) student1.clone();
student2.setName("李四");
Student student3 = (Student) student1.clone();
student3.setName("王五");
Student student4 = (Student) student1.clone();
student4.setName("赵六 ");
System.out.println("student1--->" + student1);
System.out.println("student2--->" + student2);
System.out.println("student3--->" + student3);
System.out.println("student4--->" + student4);
long end = new Date().getTime();
System.out.println("创建对象共花费了时间:" + (end -start) + " ms");
//赵六选修了课程美术
student4.setNeedExtraCourse(true);
student4.getCourse().add("美术 ");
System.out.println("\n赵六选修美术课后:");
System.out.println("student1--->" + student1);
System.out.println("student2--->" + student2);
System.out.println("student3--->" + student3);
System.out.println("student4--->" + student4);
}
}
******************【运行结果】******************
student1--->Student{name='张三', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
student2--->Student{name='李四', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
student3--->Student{name='王五', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
student4--->Student{name='赵六 ', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
创建对象共花费了时间:135
ms
赵六选修美术课后:
student1--->Student{name='张三', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
student2--->Student{name='李四', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
student3--->Student{name='王五', classId=1, needExtraCourse=false, course=[语文, 数学, 英语]}
student4--->Student{name='赵六 ', classId=1, needExtraCourse=true, course=[语文, 数学, 英语,美术
]}
可以看到,深拷贝模式 ,修改一个对象的引用类型的成员不会再影响另外对象的该成员了。
本文源码地址:
https://github.com/qinlizhong1/javaStudy/tree/master/DesignPattern/src/prototype
本文示例代码环境:
操作系统:macOs 12.1
JDK版本:12.0.1
maven版本: 3.8.4
— 完 —