1. 定义
使用原型实例指定创建对象的种类,并且通过克隆这些原型创建新的对象;原理是将一个原型对象传给要发动创建的对象,该对象通过请求原型对象克隆自己来创建过程
2. 结构图
3. 角色分析
- Prototype(抽象原型类): 它是声明克隆方法的接口,是所有具体原型类的公共父类,可以是抽象类也可以是接口;
- ConcretePrototype(具体原型类): 实现抽象原型类汇总的克隆方法,在克隆方法中返回自己的一个克隆对象 ;
- Client(客户类) : 让一个原型对象克隆自身从而创建一个新的对象,在客户类中只需要直接实例化或通过工厂方法等方式创建一个原型对象,再通过调用该对象的克隆方法即可得到多个相同的对象;
4. 通用的实现方法
class ConcretePrototype implements Prototype {
private String attr;
public void setAttr(String attr) {
this.attr = attr;
}
public String getAttr(){
return this.attr;
}
//克隆方法
public Prototype clone(){
Prototype prototype = new ConcretePrototype();
prototype.setAttr(this.attr);
return prototype;
}
}
5. jdk中clone()方法
jdk中为我们提供了克隆的方法clone(),从Object继承下来,一个对象要实现克隆,需要实现一个Cloneable()接口,该接口仅起一个标志作用,实现之后就可以调用super.clone()方法实现克隆
1) 深度克隆与浅度克隆
对某个对象进行克隆,对象的的成员变量如果包括引用类型或者数组,那么克隆的时候其实是不会把这些对象也带着复制到克隆出来的对象里面的,只是复制一个引用,这个引用指向被克隆对象的成员对象,但是基本数据类型是会跟着被带到克隆对象里面去的。而深度可能就是把对象的所有属性都统统复制一份新的到目标对象里面去; 如图所示
6. 大同小异的工作周报
1) 工作周报实现方案
2) 代码实现
浅度克隆实现周报克隆
//浅度克隆
public class WeeklyLog implements Cloneable {
private String name;
private String date;
private String content;
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;
}
public WeeklyLog clone() {
Object obj = null;
try {
obj = super.clone();
return (WeeklyLog) obj;
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
public class CloneClient {
public static void main(String[] args) {
WeeklyLog previous = new WeeklyLog();
previous.setName("张无忌");
previous.setDate("第12周");
previous.setContent("这周工作很忙,每天加班");
System.out.println("===========周报==============");
System.out.println("周次:" + previous.getDate());
System.out.println("姓名:" + previous.getName());
System.out.println("内容:" + previous.getContent());
System.out.println("===================================");
WeeklyLog next = previous.clone();
next.setDate("第13周");
System.out.println("周次:" + next.getDate());
System.out.println("姓名:" + next.getName());
System.out.println("内容:" + next.getContent());
System.out.println("===================================");
}
}
引入原型模式后,Sunny软件公司OA系统发现有些周报带有附件,如<<项目进展报告汇总表>>等;可使用深度克隆来实现
public class Attachment implements Serializable,Cloneable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String name; //附件名
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void download() {
System.out.println("下载附件,文件名为" + name);
}
}
public class WeeklyLogdeep implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
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;
}
//第一种实现方式: 使用序列化技术实现深度克隆
public WeeklyLogdeep deepClone1() throws IOException, ClassNotFoundException {
//将对象写入流中
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(this);
//将对象从流中取出
ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (WeeklyLogdeep) ois.readObject();
}
//第二种实现方式,在非基本数据类型上再次调用clone()方法
public WeekLogdeep deepClone2() {
WeeklyLogdeep wld = (WeeklyLogdeep)super.clone();
wld.setAttachment (wld.getAttachment().clone())
return wld;
}
}
7. 原型管理器的引入和实现
原型管理器(Prototype Manager)是将多个原型对象存储在一个集合中供客户端使用,它是一个专门负责克隆对象的工厂,其中定义了一个集合用于存储原型对象,如果需要某个原型对象可以通过复制集合中对应的对象来获取
8. 模拟简单公文管理器
//抽象公文接口
public interface OfficialDocument extends Cloneable {
public OfficialDocument clone();
public void display();
}
package com.zach.creator;
//可行性分析报告
public class FAR implements OfficialDocument {
@Override
public OfficialDocument clone() {
// TODO Auto-generated method stub
OfficialDocument far = null;
try {
far = (OfficialDocument) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return far;
}
@Override
public void display() {
System.out.println("可行性分析报告");
}
}
package com.zach.creator;
//软件需求规格说明书
public class SRS implements OfficialDocument {
@Override
public OfficialDocument clone() {
// TODO Auto-generated method stub
OfficialDocument srs = null;
try {
srs = (OfficialDocument) super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return srs;
}
@Override
public void display() {
System.out.println("软件需求规格说明书");
}
}
//原型管理器(饿汉式单例实现)
public class PrototypeManage {
//定义一个Hashtable,用于储存原型对象
private Hashtable<String, Object> ht = new Hashtable<>();
private static PrototypeManage pm = new PrototypeManage();
//为hashtable增加公文对象
private PrototypeManage() {
ht.put("far", new FAR());
ht.put("srs", new SRS());
}
//增加新的公文对象
public void addOfficialDocument(String key, OfficialDocument doc) {
ht.put(key, doc);
}
//通过浅克隆取新的公文对象
public OfficialDocument getOfficialDocument(String key) {
return ((OfficialDocument) ht.get(key)).clone();
}
public static PrototypeManage getPrototypeManager() {
return pm;
}
}
public class Clinet {
public static void main(String[] args) {
PrototypeManage pm = PrototypeManage.getPrototypeManager();
OfficialDocument doc1, doc2, doc3, doc4;
doc1 = pm.getOfficialDocument("far");
doc1.display();
doc2 = pm.getOfficialDocument("far");
doc2.display();
System.out.println(doc1 == doc2);
doc3 = pm.getOfficialDocument("srs");
doc3.display();
doc4 = pm.getOfficialDocument("srs");
doc4.display();
System.out.println(doc3 == doc4);
}
}
9. 原型模式总结
1) 优点
- 当创建新的对象实例比较复杂时,使用原型模式可以简化对象的创建过程
- 深度克隆的方式可以保存对象的状态,在需要的时候使用,可辅助实现撤销操作
2) 适用场景
- 创建对象的成本较大(如初始化时间长,占用CPU资源或网络资源)
- 如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占用内存较少时,可以使用原型模式配合备忘录模式实现
- 需要避免使用分层次的工厂类来创建层次对象,并且类的实例对象只有一个或很少的几个组合状态