原型模式
原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。一种创建对象的最佳方式,创建对象非常高效,无须知道对象创建的细节,这种类型的设计模式属于创建型模式。
原型模式解决的问题
高效的创建对象,不使用原型模式,创建对象,需要计算创建对象需要多少内存,如果通过原型模式创建对象不需要计算分配内存大小。就好比系统安装比较耗时,如果只是简单的复制就会快很多。
原型模式角色
抽象原型类:规定了具体原型对象必须实现的接口。
具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
访问类:使用具体原型类中的 clone() 方法来复制新的对象。
使用场景
对象之间相同或相似,即只是个别的几个属性不同的时候。
对象的创建过程比较麻烦,但复制比较简单的时候。
代码实现:
/**
* @author zengjianlu
* @date 2019/5/11
*/
public class Student implements Cloneable, Serializable {
private String name;
private List<String> interests;
public Student name(String name) {
this.name = name;
return this;
}
public Student interests(List<String> interests) {
this.interests = interests;
return this;
}
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<String> getInterests() {
return interests;
}
public void setInterests(List<String> interests) {
this.interests = interests;
}
public static Student getStudentInstance() {
List<String> interests = new ArrayList<String>();
interests.add("basketball");
interests.add("football");
return new Student().name("dada").interests(interests);
}
public static void compareObject(Student student, Student s) {
System.out.println("对比对象:" + (student == s));
System.out.println("对比对象字符串属性:" + (s.getName() == student.getName()));
System.out.println("对比对象集合属性:" + (s.getInterests() == student.getInterests()));
}
public static void changeAndCompare(Student student, Student s) {
s.setName("haha");
s.getInterests().add("music");
System.out.println(student);
System.out.println(s);
}
public Object shallowClone() throws CloneNotSupportedException {
Student proto = (Student) super.clone();
return proto;
}
public Object deepClone() throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", interests=" + interests +
'}';
}
}
1.引用的方式
/**
* @author zengjianlu
* @date 2019/5/11
*/
public class QuoteTest {
public static void main(String[] args) {
Student student = Student.getStudentInstance();
Student s = student;
Student.compareObject(student, s);
Student.changeAndCompare(student, s);
}
}
代码解读:
一个student对象,有一个字符串属性name(姓名),和一个集合属性interests(兴趣爱好),通过引用的方式,赋值对象,对比对象的地址和对象的属性地址,修改对象的属性,打印修改后的对象的值。原型模式需要实现Cloneable接口,这里只为了简单测试,真正开发过程,可以通过泛型进行抽象。
运行结果:
通过引用的方式,对象的地址一样,对象的字符串属性地址也一样,集合属性地址也一样。修改之后,原对象和引用对象都发生了变化。
2.浅拷贝的方式:
/**
* @author zengjianlu
* @date 2019/5/11
*/
public class ShallowCloneTest {
public static void main(String[] args) {
Student student = Student.getStudentInstance();
try {
Student s = (Student) student.shallowClone();
Student.compareObject(student, s);
Student.changeAndCompare(student, s);
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果:
通过浅拷贝的方式,原对象和拷贝对象的地址不同,但是属性的地址相同,修改属性之后,拷贝对象字符串修改不影响原对象属性,集合属性会影响原对象的属性。
3.深拷贝的方式
/**
* @author zengjianlu
* @date 2019/5/11
*/
public class DeepCloneTest {
public static void main(String[] args) {
Student student = Student.getStudentInstance();
try {
Student s = (Student) student.deepClone();
Student.compareObject(student, s);
Student.changeAndCompare(student, s);
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果:
通过深拷贝的方式,原对象和拷贝对象的地址不同,属性值地址也不同,拷贝对象字符串和集合属性的修改不会影响原对象,完全独立的两个个体。
泛型实现方法
public class PrototypeGenerics<T> implements Cloneable, Serializable {
public final T data;
public T getData() {
return data;
}
public PrototypeGenerics(T data) {
this.data = data;
}
public PrototypeGenerics<T> shallowClone() throws CloneNotSupportedException {
PrototypeGenerics<T> proto = (PrototypeGenerics) super.clone();
return proto;
}
public PrototypeGenerics<T> deepClone() throws IOException, ClassNotFoundException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
PrototypeGenerics<T> prototype = (PrototypeGenerics<T>)ois.readObject();
return prototype;
}
}
优缺点
优点:当创建对象的实例较为复杂的时候,使用原型模式可以简化对象的创建过程,通过复制一个已有的实例可以提高实例的创建效率。可以使用深克隆方式保存对象的状态,使用原型模式将对象复制一份并将其状态保存起来。
缺点:1、配备克隆方法需要对类的功能进行通盘考虑,这对于全新的类不是很难,但对于已有的类不一定很容易,特别当一个类引用不支持串行化的间接对象,或者引用含有循环结构的时候。 2、必须实现 Cloneable 接口。
生活中的原型模式
转载别人的原创文章就像原型模式,以作者的原创进行复制,然后进行细微的调整,甚至不调整。可以大量减少创作时间,原创是一个比较耗时的,需要准备素材,组织语言,排版等等,给那些坚持原创的作者点赞。
电视剧、电影剧本,也存在原型模式,很多电影、电视剧是根据真实事件改编,如《亲爱的》、《熔炉》、《绿皮书》、《波西米亚狂想曲》等等,以真实的故事为原型,素材来源于生活,更容易被感动。
我的启发
原型模式的两种实现,转载就好比浅拷贝,转载者需要在文末标注转载来源,较原对象有所不同。抄袭则属于深拷贝,完全一样不做任何更改。合理利用原型模式,提高生产率。
设计模式系列文章历史
java设计模式之桥接模式,策略模式旗舰版,世界没有免费的午餐
Head First 设计模式之装饰器模式,因为参与,所以认同
Head First 设计模式之观察者模式,你我都是发布者和订阅者
Head first 设计模式之策略模式,来源于生活,用之于生活
欢迎扫码,交流探讨
扫码关注,输入“java”,获得推荐书籍