什么是原型模式
原型模式是用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
为什么要用原型模式
现在有一份纸质版问卷,发问卷的人希望你可以多找一些人填写问卷,你应该怎么做?
答案很简单,找个复印店复印几份嘛:
初步实现
问卷类:
public class Paper {
private String title;
private String author;
private String question;
public Paper(String title, String author, String question) {
this.title = title;
this.author = author;
this.question = question;
}
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
public String getQuestion() { return question; }
public void setQuestion(String question) { this.question = question; }
@Override
public String toString() {
return "Paper{" +
"title='" + title + '\'' +
", author='" + author + '\'' +
", question='" + question + '\'' +
'}';
}
}
复印机:
public class Copier {
public static void main(String[] args) throws CloneNotSupportedException {
Paper paper1 = new Paper("DesignPattern", "Mingyi", "prototype");
Paper paper2 = new Paper(paper1.getTitle(), paper1.getAuthor(), paper1.getQuestion());
Paper paper3 = new Paper(paper1.getTitle(), paper1.getAuthor(), paper1.getQuestion());
Paper paper4 = new Paper(paper1.getTitle(), paper1.getAuthor(), paper1.getQuestion());
Paper paper5 = new Paper(paper1.getTitle(), paper1.getAuthor(), paper1.getQuestion());
System.out.println(paper1 + ", hashCode:" + paper1.hashCode()); //1209271652
System.out.println(paper2 + ", hashCode:" + paper2.hashCode()); //1698156408
System.out.println(paper3 + ", hashCode:" + paper3.hashCode()); //1740035246
System.out.println(paper4 + ", hashCode:" + paper4.hashCode()); //884457408
System.out.println(paper5 + ", hashCode:" + paper5.hashCode()); //913190639
}
}
分析
- 确实做到了“复印”的效果。
- 每一次的“复印”都是让复印机读取原件的相应信息再打印到白纸上的,这是不符合逻辑的。
- 如果要复印的对象的属性很多、构造起来比较复杂,那么new的时候就会十分麻烦。
为了解决复印麻烦的问题,可以使用原型模式来实现。
使用原型模式实现
- 让Paper类实现Cloneable接口(这是一种约定,即使clone()方法在Object类内,但是不实现的话在调用clone()方法时会抛出CloneNotSupportedException异常)
- 重写clone()方法
Paper类:
public class Paper implements Cloneable{
private String title;
private String author;
private String question;
public Paper(String title, String author, String question) {
this.title = title;
this.author = author;
this.question = question;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getAuthor() { return author; }
public void setAuthor(String author) { this.author = author; }
public String getQuestion() { return question; }
public void setQuestion(String question) { this.question = question; }
@Override
public String toString() {
return "Paper{" +
"title='" + title + '\'' +
", author='" + author + '\'' +
", question='" + question + '\'' +
'}';
}
}
复印机:
public class Copier {
public static void main(String[] args) throws CloneNotSupportedException {
Paper paper1 = new Paper("DesignPattern", "Mingyi", "prototype");
Paper paper2 = (Paper) paper1.clone();
Paper paper3 = (Paper) paper1.clone();
Paper paper4 = (Paper) paper1.clone();
Paper paper5 = (Paper) paper1.clone();
System.out.println(paper1 + ", hashCode:" + paper1.hashCode()); //1209271652
System.out.println(paper2 + ", hashCode:" + paper2.hashCode()); //1698156408
System.out.println(paper3 + ", hashCode:" + paper3.hashCode()); //1740035246
System.out.println(paper4 + ", hashCode:" + paper4.hashCode()); //884457408
System.out.println(paper5 + ", hashCode:" + paper5.hashCode()); //913190639
}
}
分析
- 实现了复印功能。
- 复印机复印时只需要“复印”(调用clone()方法)即可,不需要关注原件上的内容究竟是什么(自己new)。
- 现在的clone是一种浅拷贝方式(因为直接使用的super.clone()方法)。
深拷贝与浅拷贝
浅拷贝:
两个引用,引用了同一块内存空间。
深拷贝:
两个引用,各自引用一块内存空间,这两块内存空间内的值相同。
对比:
- 对于基本数据类型以及String类型,深拷贝与浅拷贝没有区别。
- 浅拷贝由于引用了同一块内存空间,一方做出修改时,其他引用也会得到被修改的数据。
- 深拷贝引用不同的内存空间,每个引用对自己的空间的引用是独立的,互不影响。
深拷贝实现:
假设对于上面的例子,question不是String类型,而是一个Question类型:
Question类:
public class Question implements Cloneable{
private String question;
public Question(String question) {
this.question = question;
}
@Override
protected Object clone() throws CloneNotSupportedException { //需要重写clone方法供Paper类调用
return super.clone();//由于只有一个String类型的属性,浅拷贝即可
}
public String getQuestion() {
return question;
}
public void setQuestion(String question) {
this.question = question;
}
}
Paper类:
public class Paper implements Cloneable{
private String title;
private String author;
private Question question;
public Paper(String title, String author, Question question) {
this.title = title;
this.author = author;
this.question = question;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Paper paper = (Paper) super.clone();
paper.setQuestion((Question) this.question.clone());//调用question的clone方法,获取一个具有相同属性但内存空间不同的实例。
return paper;
}
// ...
}
这样即实现了Paper的深拷贝。
当然实现深拷贝的方式还有很多,例如可以通过序列化的方式实现,这里就不做赘述了。