一、原型模式概述
原型(Prototype)模式的定义:用一个已经创建的实例作为原型,通过复制该原型对象来创建一个和原型相同或相似的新对象。在这里,原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。(对象创建型)
- 工作原理:
- 将一个原型对象传给要发动创建的对象(即客户端对象),这个要发动创建的对象通过请求原型对象复制自己来实现创建过程。
- 创建新对象(也称克隆对象)的工厂就是原型类自身,工厂方法由负责复制原型对象的克隆方法来实现。
- 通过克隆方法所创建的对象是全新的对象,它们在内存中拥有新的地址,每个克隆对象都是独立的。
- 通过不同的方式对克隆对象进行修改以后,可以得到一系列相似但不完全相同的对象。
-
浅克隆与深克隆:
- 浅克隆:当原型对象被复制时,只复制它本身和其中包含的值类型的成员变量,而引用类型的成员变量并没有复制。
- 深克隆:除了对象本身被复制外,对象所包含的所有成员变量也将被复制。
-
原型管理器:
- 将多个原型对象存储在一个集合中提供客户端使用,它是一个专门负责克隆对象的工厂,其中定义了一个集合用于存储原型对象,如果需要某个原型对象的一个克隆,可以通过复制集合中对应的原型对象来获得。
-
原型模式的优缺点:
-
优点:
- 1.简化对象的创建过程,通过复制一个已有实例可以提高新实例的创建效率;
- 2.扩展性好;
- 3.提供了简化的创建结构,原型模式中的产品的复制时通过封装在原型类中的克隆方法实现的,无须专门的工厂类来创建产品;
- 4.可以使用深克隆的方式保存对象的状态,以便在需要的时候使用,可辅助实现撤销操作。
-
缺点:
- 1.需要为每一个类配备一个克隆方法,而且该克隆方法位于一个类的内部,当已有对象的类进行改造时们需要修改源代码,违背了开闭原则;
- 2.在实现深克隆时需要编写较为复杂的代码,而且当对象之间存在多重的嵌套引用时,为了实现深克隆,每一层对象对应得类都必须支持深克隆,实现起来可能会比较麻烦。
-
优点:
-
适用环境:
- 1.创建新对象成本比较大,新对象可以通过复制已有对象来获得,如果相似对象,则可以对其成员变量稍作修改;
- 2.系统要保存对象得状态,而对象得状态变化很小;
- 3.需要便面使用分层次得工厂类来创建分层次得对象;
- 4.ctrl+c->ctrl+v。
二、代码实现
原型模式包含三个角色:
- 访问类(客户类):提出创建对象的请求,使用具体原型类中的 clone() 方法来复制新的对象。
- 抽象原型(Prototype)角色:此角色定义了的具体原型类所需的实现的方法。也就是定义一个文件,说明一下它有被克隆复制的功能。
- 具体原型(Concrete Prototype)角色:实现抽象原型角色的克隆接口。就是我们的文件实现了可以被复制的功能。
我们会发现其实原型模式的核心就是Prototype(抽象原型),他需要继承Cloneable接口,并且重写Object类中的clone方法才能有复制粘贴的功能。
2.1 demo
2.1.1 抽象原型角色
package prototype.demo;
//抽象原型角色
public interface Prototype {
public Prototype clone();
}
2.1.2 具体原型角色
package prototype.demo;
//具体原型角色
public class ConcretePrototype implements Prototype{
private int dataInt=1;
private A dataA=new A();
public String toString() {
return "ConcretePrototype"+"["+",dataInt"+dataInt
+",dataA_address="+dataA
+",dataA="+dataA.getChar_a()+
"]";
}
public int getDataInt() {
return dataInt;
}
public void setDataInt(int dataInt) {
this.dataInt = dataInt;
}
public A getDataA() {
return dataA;
}
public void setDataA(A dataA) {
this.dataA = dataA;
}
//克隆方法 shallow
public Prototype clone() {
ConcretePrototype copy=new ConcretePrototype();
copy.setDataInt(this.getDataInt());
copy.setDataA(this.getDataA());
return copy;
}
//克隆方法 deep
public Prototype clone2() {
ConcretePrototype copy=new ConcretePrototype();
copy.setDataInt(this.getDataInt());
A ta=new A();
ta.setChar_a(this.getDataA().getChar_a());
copy.setDataA(this.getDataA());
return copy;
}
}
package prototype.demo;
//封装的方法
public class A {
char char_a;
public char getChar_a() {
return char_a;
}
public void setChar_a(char char_a) {
this.char_a = char_a;
}
public A(char char_a) {
super();
this.char_a=char_a;
}
public A() {
this.char_a='a';
}
}
2.1.3 main方法实现原型模式(Client)
package prototype.demo;
public class Client {
public static void main(String[] args) {
// TODO 自动生成的方法存根
/*ConcretePrototype p=new ConcretePrototype();
ConcretePrototype copy=(ConcretePrototype) p.clone();
ConcretePrototype copy2=(ConcretePrototype) p.clone();
System.out.println(p.toString());
System.out.println(copy.toString());
System.out.println(copy2.toString());
copy.setDataInt(2);
A a=new A('b');
copy.setDataA(a);
System.out.println("-------------------------------");
System.out.println(p.toString());
System.out.println(copy.toString());
System.out.println(copy2.toString());*/
ConcretePrototype p=new ConcretePrototype();
ConcretePrototype copy=(ConcretePrototype) p.clone2();
ConcretePrototype copy2=(ConcretePrototype) p.clone2();
System.out.println(p.toString());
System.out.println(copy.toString());
System.out.println(copy2.toString());
copy.setDataInt(2);
A a=new A('b');
copy.setDataA(a);
System.out.println("-------------------------------");
System.out.println(p.toString());
System.out.println(copy.toString());
System.out.println(copy2.toString());
}
}
2.4 UML图
2.2 浅克隆
2.2.1 抽象原型角色
java.io.Serializable这个接口。
2.2.2 具体原型角色
package prototype.deepclone;
import java.io.Serializable;
//实现了Serializable这个接口
public class Attachment implements Serializable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void download() {
System.out.println("下载附件,文件名为:"+name);
}
}
package prototype.shallowclone;
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 WeeklyLog clone(){
// TODO 自动生成的方法存根
try {
return (WeeklyLog)super.clone();
} catch (CloneNotSupportedException e) {
// TODO 自动生成的 catch 块
e.printStackTrace();
return null;
}
}
}
2.2.3 Client
package prototype.shallowclone;
public class Client {
public static void main(String[] args) {
// TODO 自动生成的方法存根
WeeklyLog obj=new WeeklyLog();
Attachment att=new Attachment();
att.setName("obj");
obj.setAttachment(att);
WeeklyLog copy=obj.clone();
obj.getAttachment().download();
copy.getAttachment().download();
}
}
2.3 深克隆
2.2.1 抽象原型角色
java.io.Serializable这个接口。
2.2.2 具体原型角色
package prototype.deepclone;
import java.io.Serializable;
//实现了Serializable这个接口
public class Attachment implements Serializable{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void download() {
System.out.println("下载附件,文件名为:"+name);
}
}
package prototype.deepclone;
import java.io.*;
public class WeeklyLog implements Serializable{
//简化设计,定义一个附件
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;
}
//使用序列化技术实现克隆
protected WeeklyLog deepClone() throws IOException,ClassNotFoundException{
// TODO 自动生成的方法存根
//将对象写入流中
ByteArrayOutputStream bao=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bao);
oos.writeObject(this);
//将对象从流中取出
ByteArrayInputStream bis=new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bis);
return (WeeklyLog)ois.readObject();
}
}
2.2.3 Client
package prototype.deepclone;
public class Client {
public static void main(String[] args) {
// TODO 自动生成的方法存根
WeeklyLog log_previous,log_new=null;
log_previous=new WeeklyLog();//创建原型对象
Attachment attachment=new Attachment();//创建附件对象
attachment.setName("aaa");
log_previous.setAttachment(attachment);//将附件添加到周报中
try {
log_new=log_previous.deepClone();//调用深克隆方法
}catch(Exception e) {
System.out.println("克隆失败!");
}
//比较周报
System.out.println("周报是否相同?"+(log_previous==log_new));
//比较附件
System.out.println("附件是否相同?"+(log_previous.getAttachment()==log_new.getAttachment()));
log_previous.getAttachment().download();
log_new.getAttachment().download();
}
}