原型模式
简介
原型模式是一种个对象创建型模式,它的工作原理很简单:将一个原型对象传给要发动创建的对象(即客户端对象),这个要发动创建的对象通过请求原型对象赋值自己来实现创建过程。由于软件系统中经常会遇到需要创建多个相同或者相似对象的情况,因此原型模式在软件开发中具有较高的使用频率。元凶模式是一种另类的创建想模式,创建新对象(也称为克隆对象)的工厂就是原型类自身,工厂方法由负责赋值原型对象的克隆方法来实现。
需要注意的是通过克隆方法所创建的对象是全新的对象,他们在内存中拥有新的地址,通常克隆所产生的对象进行修改不会对原型对象造成任何影响,每一个克隆对象都是相互独立的。通过不同的方法对克隆对象进行修改以后,可以得到一系列相似但不完全相同的对象。
定义
使用原型实例指定待创建对象的类型,并且通过复制这个原型来创建新的对象。
**Prototype Pattern: **Specify the kinds of objects to create using a prototypical instance,and create new objects by copying this prototype.
Prototype抽象原型类:它是声明克隆方法的接口,是所有具体原型类的公共父类,它可以是抽象类也可以是接口,甚至还可以是具体实现类。
ConcretePrototype(具体原型类):它实现在抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象
Client(客户类):在客户类中让一个原型对象克隆自身从而创建一个新的对象。
浅克隆与深克隆
根据在复制原型对象的同时是否复制包含原型对象中引用类型的成员变量,原型模式的克隆机制分为两种:浅克隆(Shallow Clone)和深克隆(Deep Clone)。
浅克隆
在浅克隆中基本数据类型primitive,int short long float double boolean char byte 会进行复制,从原对象复制一份给克隆对象,而引用类型(也就是对象包括类,接口,数组等复杂数据类型),并不会复制只是将这些对象的地址指针给了新的对象让新对象的成员变量指向这些内存地址。
深克隆
深克隆就是将基本数据类型和对象都重新创建一份给克隆对象
Java语言中的clone()方法和Cloneable接口
在Java语言中,所有的Java类均继承自java.lang.Object类,Object类中提供了一个clone()方法,可以将一个Java对象复制一份。因此在Java中可以直接使用Object提供的clone()方法来实现对象的浅克隆。
需要注意的是能够实现对象克隆的Java类必须实现一个标识接口Cloneable,表示这个Java类支持被复制。如果一个类没有实现这个接口但是调用了clone()方法,Java编译器将抛出一个CloneNotSupportedException异常。
注意
- 对任何对象x,都有x.clone()!=x,即克隆对象与原型对象不是同一个对象。
- 对任何对象x,都有x.clone().getClass()==x.getClass(),.即克隆对象与原型对象的类型一样。
- 如果对象x的equals()方法定义恰当,那么x.clone().equals(x)应该成立。(如果未恰当定义使用默认的,在比较对象时会出现地址比对产生不一致
为了获取对象的一个克隆,可以直接利用Object类的clone()方法,具体步骤如下:
- 在派生类中覆盖基类的clone()方法并声明未public
- 在派生类的clone方法中调用super.clone().
- 派生类实现Cloneable接口
- 也可以直接实现Cloneable接口然后重写clone方法就是重写的方法默认是 protected类型的
代码
经典实现
package priv.wzb.design_pattern.createdpattern.protoyupepattern.classic;
/**
* @author Satsuki
* @time 2020/2/3 13:59
* @description:
* 抽象原型类:它是声明克隆方法的接口,是所有具体原型类的公共父类,
* 它可以是抽象类也可以是接口,甚至还可以是具体实现类。
*/
public abstract class Prototype {
public abstract Prototype clone();
}
package priv.wzb.design_pattern.createdpattern.protoyupepattern.classic;
/**
* @author Satsuki
* @time 2020/2/3 14:02
* @description:
* 具体原型类,它实现在抽象原型类中声明的克隆方法,
* 在克隆方法中返回自己的一个克隆对象
*/
public class ConcretePrototype extends Prototype {
private String attr;
public String getAttr() {
return attr;
}
public void setAttr(String attr) {
this.attr = attr;
}
/**
* 克隆方法
* @return 原型的克隆对象
*/
@Override
public Prototype clone() {
Prototype prototype = new ConcretePrototype();
((ConcretePrototype) prototype).setAttr(this.attr);
return prototype;
}
}
package priv.wzb.design_pattern.createdpattern.protoyupepattern.classic;
/**
* @author Satsuki
* @time 2020/2/3 14:05
* @description:
* 客户类,使用具体原型类创建一个自己的复制对象
*/
public class Client {
public static void main(String[] args) {
ConcretePrototype prototype = new ConcretePrototype();
prototype.setAttr("Sunny");
ConcretePrototype copy = (ConcretePrototype) prototype.clone();
}
}
例子:OA系统中周报有一部分附件总是相同,重写创建产生冗余操作。使用原型模式,克隆出一份不同的周报但是周报中的附件相同
package priv.wzb.design_pattern.createdpattern.protoyupepattern.example;
/**
* @author Satsuki
* @time 2020/2/3 14:33
* @description: 附件类
*/
public class Attachment {
private String name;// 附件名
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void download(){
System.out.println("下载附件,文件名为" + name);
}
}
package priv.wzb.design_pattern.createdpattern.protoyupepattern.example;
/**
* @author Satsuki
* @time 2020/2/3 14:34
* @description:
* 工作周报类,充当原型角色。在真实环境下该类将比较复杂,考虑到代码的可读性,
* 再次只列出部分与模式相关的核心代码
*/
public class WeeklyLog implements Cloneable{
// 为了简化设计和实现,假设一份工作周报中只有一个附件对象
// 在实际情况中可以包含多个附件,使用集合对象实现
private Attachment attachment;
private String name;
private String date;
private String content;
public Attachment getAttachment() {
return attachment;
}
public void setAttachment(Attachment attachment) {
this.attachment = attachment;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
// 使用clone()方法实现浅克隆
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
package priv.wzb.design_pattern.createdpattern.protoyupepattern.example;
/**
* @author Satsuki
* @time 2020/2/3 14:37
* @description:
* 客户端测试类
*/
public class Client {
public static void main(String[] args) {
WeeklyLog logPrevious,logNew;
// 创建原型对象
logPrevious = new WeeklyLog();
// 创建附件对象
Attachment attachment = new Attachment();
// 将附件添加到周报
logPrevious.setAttachment(attachment);
try {
// 调用克隆方法创建克隆对象
logNew = (WeeklyLog) logPrevious.clone();
//比较周报
System.out.println("周报是否相同? " + (logPrevious==logNew));
// 比较附件
System.out.println("附件是否相同?" + (logPrevious.getAttachment()==logNew.getAttachment()));
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
总结
优点
- 当创建新的对象实例较为复杂,使用原型模式可以简化对象创建过程,通过复制一个已有实例可以提高新实例的创建效率。
- 扩展性好
- 提供了简化的创建结构
- 可以使用深克隆保存对象状态。可辅助实现撤销
缺点:
- 需要未每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当对已有的类进行改造时需要修改源代码,违背了开闭原则。
- 在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重嵌套引用时,为了是极限深克隆,每一层对象对应的类都必须支持深克隆,实现麻烦
.
欢迎大家star fork