原型模式(Prototype Pattern)用于创建重复的对象,同时又能保证性能。
本体给外部提供一个克隆体进行使用。
比如我们做一个SjdwzMybatis,用来操作数据库,从数据库里面查出很多记录,其中很多记录改变很少。每次查数据库,把所有数据都封装一个对象,然后返回。假设有很多线程,来查如下记录:
Student student = new Student("张三","男");
如果每次都创建对象封装并返回,这样系统就会有很多student;这样就会浪费内存。
Student类如下:
public class Student {
private String name;
private Integer age;
public Student() {
System.out.println("创建了Student对象");
}
//省略getter() 、 setter() toString()
}
SjdwzMybatis如下:
public class SjdwzMybatis {
/**
* 通过name获取Student
*/
public Student queryStudent(String name){
return queryStudentFromDB(name);
}
/**
* 演示从数据库查Student
*/
private Student queryStudentFromDB(String name) {
//简单演示,查询到了
System.out.println("从数据库查询到了:"+name);
Student student = new Student();
student.setName(name);
student.setAge(16);
return student;
}
}
测试类:
public class ProtoTypeTest {
public static void main(String[] args) {
SjdwzMybatis sjdwzMybatis = new SjdwzMybatis();
Student stu1 = sjdwzMybatis.queryStudent("zhangsan");
Student stu2 = sjdwzMybatis.queryStudent("zhangsan");
Student stu3 = sjdwzMybatis.queryStudent("zhangsan");
Student stu4 = sjdwzMybatis.queryStudent("zhangsan");
}
}
这样会有大量具有相同属性的student被外部创建,同时查库次数过多。
我们是否能设计一个缓存,来保存查过的内容,再查相同的记录时,可以很快拿到原来的原型对象呢?
那我们的SjdwzMybatis便变成了如下代码:
public class SjdwzMybatis {
//缓存
private Map<String,Student> stuCache = new HashMap<>();
/**
* 通过name获取Student
*/
public Student queryStudent(String name){
if(stuCache.containsKey(name)){
return stuCache.get(name);
}else{
return queryStudentFromDB(name);
}
}
/**
* 演示从数据库查Student
*/
private Student queryStudentFromDB(String name) {
//简单演示,查询到了
System.out.println("从数据库查询到了:"+name);
Student student = new Student();
student.setName(name);
student.setAge(16);
//存入内存
stuCache.put(name,student);
return student;
}
}
但是这是否会有问题呢?
如果我们把stu1的属性改了,那么stu2、stu3、stu4的属性也会被改变,这会影响到我们缓存里的数据,造成脏缓存数据;同时我们查出来的内容,并没有提交修改,不能就把原数据给修改掉。
我们把Student修改成如下代码,这便是原型模式:
//实现Cloneable接口,这只是一个标记,还需要重写clone()方法
public class Student implements Cloneable{
private String name;
private Integer age;
//重写clone方法
@Override
protected Object clone() throws CloneNotSupportedException {
Student student = new Student();
student.setName(this.name);
student.setAge(this.age);
return student;
}
}
然后SjdwzMybatis修改为如下代码:
public class SjdwzMybatis {
//缓存
private Map<String,Student> stuCache = new HashMap<>();
/**
* 通过name获取Student
*/
public Student queryStudent(String name) throws CloneNotSupportedException {
if(stuCache.containsKey(name)){
return (Student) stuCache.get(name).clone();
}else{
return queryStudentFromDB(name);
}
}
/**
* 演示从数据库查Student
*/
private Student queryStudentFromDB(String name) throws CloneNotSupportedException {
//简单演示,查询到了
System.out.println("从数据库查询到了:"+name);
Student student = new Student();
student.setName(name);
student.setAge(16);
//存入内存
stuCache.put(name,(Student) student.clone());
return student;
}
}
从数据库查出来放入缓存的对象与从缓存取出来的都是clone出来的。
可以看到,我们对stu1修改,并不会影响其他的数据了。