目录
概念
原型模式是一种创建型设计模式,它用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。原型模式(Prototype Pattern)的简单程度仅次于单例模式和迭代器模式。正是由于简单,使用的场景才非常地多。
原型模式是一种“另类”的创建型模式,创建克隆对象的工厂就是原型类自身,工厂方法由克隆方法来实现。
Prototype模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。
应用场景
1、对象之间相同或相似,即只是个别的几个属性不同的时候。
2、对象的创建过程比较麻烦,但复制比较简单的时候。
3、创建新对象成本比较大。
4、一个对象有多个修改人。
5、对对象的状态进行记录。
分类
原型模式的核心就是拷贝对象,那么我们能拷贝一个对象实例的什么内容呢?这就要区分深拷贝和浅拷贝之分了。
浅拷贝
我们只拷贝对象中的基本数据类型(8种),对于数组、容器、引用对象等都不会拷贝。需要注意的是,浅克隆方式创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。
深拷贝
不仅能拷贝基本数据类型,还能拷贝那些数组、容器、引用对象等。需要注意的是,深克隆方式创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。
优缺点
优点
1、性能优良
原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。另外,当创建新的对象实例较为复杂时,使用原型模式可以简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率。
2、逃避构造函数的约束
这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的。 优点就是减少了约束,缺点也是减少了约束,需要大家在实际应用时考虑。
3、扩展性好
由于在原型模式中提供了抽象原型类,在客户端可以针对抽象原型类进行编程,而将具体原型类写在配置文件中,增加或减少产品类对原有系统都没有任何影响。
4、提供了简化的创建结构方式
原型模式提供了简化的创建结构,工厂方法模式常常需要有一个与产品类等级结构相同的工厂等级结构,而原型模式就不需要这样,原型模式中产品的复制是通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品。
缺点
1、违反开闭原则
需要为每一个类配置一个克隆方法,而且该克隆方法位于类的内部,当对已有类进行改造的时候,需要修改代码,违反了开闭原则。
2、多重嵌套引用时,实现麻烦
在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了实现深克隆,每一层对象对应的类都必须支持深克隆,实现起来会比较麻烦。
3、无法和单例模式组合使用
由于使用原型模式复制对象时不会调用类的构造方法,所以原型模式无法和单例模式组合使用。
角色构成
Prototype(抽象原型类)
声明克隆方法的接口,是所有具体原型类的公共父类,它可是抽象类也可以是接口,甚至可以是具体实现类。
ConcretePrototype(具体原型类)
它实现抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。
Client(客户端访问类)
在客户类中,让一个原型对象克隆自身从而创建一个新的对象。
灵魂拷问
为什么不用new直接新建对象,而要用原型模式?
用new新建对象不能获取当前对象运行时的状态,其次就算new了新对象,在将当前对象的值复制给新对象,效率也不如原型模式高。
为什么不直接使用拷贝构造函数,而要使用原型模式?
原型模式与拷贝构造函数是不同的概念,拷贝构造函数涉及的类是已知的,原型模式涉及的类可以是未知的(基类的拷贝构造函数只能复制得到基类的对象)。
原型模式使用小结
原型模式大都是用来创建重复的同类型但属性稍微不同的对象,但缺点就是要考虑深克隆的场景,如果一个类很复杂,里面有许多需要深度克隆的变量引用时,那么对于这个类而言,进行克隆的时候要妥善处理好变量中的深度克隆。
代码实现
浅克隆
ShallowClassroom.java
package pattern.prototype.shallow;
/**
* 〈功能详细描述〉
*
* @author 刘斌
* @date 2020/3/7
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class ShallowClassroom {
private String className;
public ShallowClassroom() {
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"ClassName\":\"").append(className).append('\"');
sb.append('}').append(super.toString());
return sb.toString();
}
}
ShallowStudent.java
package pattern.prototype.shallow;
/**
* 〈功能详细描述〉
*
* @author 刘斌
* @date 2020/3/7
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class ShallowStudent implements Cloneable {
private String name;
private Integer age;
private ShallowClassroom classroom;
public ShallowStudent() {
System.out.println("ShallowStudent类的无参构造器...");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public ShallowClassroom getClassroom() {
return classroom;
}
public void setClassroom(ShallowClassroom classroom) {
this.classroom = classroom;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"Name\":\"").append(name).append('\"');
sb.append(",\"Age\":").append(age);
sb.append(",\"Classroom\":").append(classroom);
sb.append('}').append(" ").append(super.toString());
return sb.toString();
}
}
ShallowCloneClient
package pattern.prototype.shallow;
/**
* 〈功能详细描述〉
*
* @author 刘斌
* @date 2020/3/7
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class ShallowCloneClient {
/**
* 案例:一个班级有多个学生,每个学生只对应一个班级
*/
public static void main(String[] args) throws CloneNotSupportedException {
ShallowClassroom classroom = new ShallowClassroom();
classroom.setClassName("高三<7>班");
ShallowStudent student1 = new ShallowStudent();
student1.setName("Jone");
student1.setAge(20);
student1.setClassroom(classroom);
ShallowStudent student2 = (ShallowStudent) student1.clone();
ShallowStudent student3 = (ShallowStudent) student1.clone();
// 先观察clone出来的对象地址
System.out.println("各个对象的地址");
System.out.println(student1.toString());
System.out.println(student2.toString());
System.out.println(student3.toString());
System.out.println("修改student2和student3的属性");
student2.setName("Marry");
student2.setAge(18);
student3.setName("Stan");
student3.setAge(19);
System.out.println(student1);
System.out.println(student2);
System.out.println(student3);
//浅克隆:对一处改变,其它拥有相同引用的对象也都改变,所以上面所有的课程都变成了一样。
System.out.println("Marry被调到高三<1>班");
student2.getClassroom().setClassName("高三<1>班");
System.out.println(student1);
System.out.println(student2);
System.out.println(student3);
}
}
浅克隆控制台输出
深克隆
DeepClassroom.java
package pattern.prototype.deep;
/**
* 〈功能详细描述〉
*
* @author 刘斌
* @date 2020/3/7
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class DeepClassroom implements Cloneable {
private String className;
public DeepClassroom() {
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"ClassName\":\"").append(className).append('\"');
sb.append('}').append(super.toString());
return sb.toString();
}
}
DeepStudent.java
package pattern.prototype.deep;
/**
* 〈功能详细描述〉
*
* @author 刘斌
* @date 2020/3/7
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class DeepStudent implements Cloneable {
private String name;
private Integer age;
private DeepClassroom classroom;
public DeepStudent() {
System.out.println("DeepStudent类的无参构造器...");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public DeepClassroom getClassroom() {
return classroom;
}
public void setClassroom(DeepClassroom classroom) {
this.classroom = classroom;
}
@Override
protected Object clone() throws CloneNotSupportedException {
DeepStudent student = (DeepStudent) super.clone();
student.setClassroom((DeepClassroom) student.classroom.clone());
return student;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"Name\":\"").append(name).append('\"');
sb.append(",\"Age\":").append(age);
sb.append(",\"Classroom\":").append(classroom);
sb.append('}').append(" ").append(super.toString());
return sb.toString();
}
}
DeepCloneClient.java
package pattern.prototype.deep;
/**
* 〈功能详细描述〉
*
* @author 刘斌
* @date 2020/3/7
* @see [相关类/方法](可选)
* @since [产品/模块版本] (可选)
*/
public class DeepCloneClient {
public static void main(String[] args) throws CloneNotSupportedException {
DeepStudent student1 = new DeepStudent();
DeepClassroom classroom = new DeepClassroom();
classroom.setClassName("高三<7>班");
student1.setName("Jone");
student1.setAge(20);
student1.setClassroom(classroom);
DeepStudent student2 = (DeepStudent) student1.clone();
DeepStudent student3 = (DeepStudent) student1.clone();
// 先观察clone出来的对象地址
System.out.println("各个对象的地址");
System.out.println(student1.toString());
System.out.println(student2.toString());
System.out.println(student3.toString());
System.out.println("修改student2和student3的属性");
student2.setName("Marry");
student2.setAge(18);
student3.setName("Stan");
student3.setAge(19);
System.out.println(student1);
System.out.println(student2);
System.out.println(student3);
System.out.println("Marry被调到高三<1>班");
student2.getClassroom().setClassName("高三<1>班");
System.out.println(student1);
System.out.println(student2);
System.out.println(student3);
}
}
深克隆控制台输出
由深克隆控制台输出结果,可以看出,使用强克隆可以为引用对象开辟一块内存地址而不是像浅克隆那样指向同一个引用对象。