Java设计模式-原型模式
- 原型模式是用于创建重复的对象,同时又能保证性能。
- 这种设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
- 这种模式是实现了一个原型接口,该接口用于创建当前对象的克隆。当直接创建对象的代价比较大时,则采用这种模式。例如,一个对象需要在一个高代价的数据库操作之后被创建。我们可以缓存该对象,在下一个请求时返回它的克隆,在需要的时候更新数据库,以此来减少数据库调用。
介绍
-
目的:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
-
何时使用
- 当一个系统应该独立于它的产品创建,构成和表示时
- 当要实例化的类是在运行时刻指定时,例如,通过动态装载
- 为了避免创建一个与产品类层次平行的工厂类层次时
- 当一个类的实例只能有几个不同状态组合中的一种时。建立相应数目的原型并克隆它们可能比每次用合适的状态手工实例化该类更方便一些。
-
优点
- 性能提高
- 逃避构造函数的约束
-
缺点
- 配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候
- 必须实现Cloneable接口
-
使用场景
- 资源优化场景
- 类初始化需要消化非常多的资源,这个资源包括数据、硬件资源等
- 性能和安全要求的场景
- 通过new 产生一个对象需要非常繁琐的数据准备和访问权限,则可以使用原型模式
- 一个对象多个修改者的场景
- 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象工调用者使用
- 在实际项目中,原型模式很少单独出现,一般适合工厂方法模式一起出现,通过clone的方法创建一个对象,然后由工厂方法提供给调用者。
-
注意:与通过对一个类进行实例化来构造新对象不同的是,原型模式是通过拷贝一个现有对象生成新对象的。浅拷贝实现Cloneable,重写和深拷贝是通过实现Serializable读取二进制流。
分析
-
Object的clone方法是一个本地方法,它可以直接操作内存中的二进制流,所以性能相对new实例化来说,更加优秀
-
一个对象通过new实例化创建过程为:
- 在内存中开辟一块空间
- 在开辟的空间中创建对象
- 调用对象的构造函数进行初始化对象
-
一个对象通过clone创建过程为:
- 根据原对象内存大小开辟一块内存空间
- 复制已有对象,克隆对象中所有属性值
深拷贝和浅拷贝(一个例子)
- Student类中有一个Teaacher对象,这两个类都实现Cloneable接口
@Getter
@Setter
public class Student implements Cloneable{
/**
* 学生姓名
*/
private String name;
/**
* 学生所属的老师
*/
private Teacher teacher;
/**
* 重写克隆方法,对学生进行克隆
*/
public Student clone() {
Student student = null;
try {
student = (Student) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return student;
}
}
@Getter
@Setter
public class Teacher implements Cloneable{
/**
* 老师姓名
*/
private String name;
/**
* 重写克隆方法,对老师类进行克隆
*/
public Teacher clone() {
Teacher teacher= null;
try {
teacher= (Teacher) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return teacher;
}
}
- 然后定义一个学生和一个老师,并让其关联在一起
public class Test {
public static void main(String args[]) {
// 定义老师1
Teacher teacher = new Teacher();
teacher.setName("刘老师");
// 定义学生1
Student stu1 = new Student();
stu1.setName("test1");
// 老师1和学生1进行关联
stu1.setTeacher(teacher);
// 复制学生1,生成学生2
Student stu2 = stu1.clone();
stu2.setName("test2");
// 修改学生2的老师
stu2.getTeacher().setName("王老师");
// 查看修改结果
System.out.println("学生" + stu1.getName() + "的老师是:" + stu1.getTeacher().getName());
System.out.println("学生" + stu1.getName() + "的老师是:" + stu2.getTeacher().getName());
}
}
-
最后发现在给学生2修改老师的时候,学生1的老师也跟着修改了,这就使浅拷贝带来的问题
-
这时候,修改student类的clone方法如下
/**
* 重写克隆方法,对学生和老师都进行克隆
*/
public Student clone() {
Student student = null;
try {
student = (Student) super.clone();
// 克隆 teacher 对象
Teacher teacher = this.teacher.clone();
student.setTeacher(teacher);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return student;
}