一、原型模式
1、定义
原型模式(Prototype Pattern)指原型实例指定创建对象的种类,并且通过复制这些原型创建新的对象,属于创建型设计模式。
原型模式的核心在于复制原型对象。
2、结构
(1)模式的结构
主要角色如下:
- 客户类(Client):客户类提出创建对象的请求
- 抽象原型(Iprototype):规定复制接口。
- 具体原型(ConcretePrototype):被复制的对象。
注意:
不是通过直接 new关键字而是通过复制对象来实现创建对象的模式被称作原型模式。
3、优缺点
优点:
- Java自带的原型模式基于内存二进制流的复制,在性能上比直接 new更好。
- 通过深拷贝方式保存对象状态,简化创建对象的过程。
缺点:
- 需要为每一个类都配置一个 clone方法。
- clone方法位于类的内部,如果对已有类进行改造时,需要修改代码,违背了开闭原则。
- 当对象之间存在多重嵌套引用时,使用深拷贝会比较麻烦,每一层对象对应的类都必须支持深拷贝。
4、使用场景
- 创建对象成本较大,需要优化资源
- 系统中大量使用该类对象,并且各个调用者都需要它的属性重复赋值。
5、在框架源码中使用
二、模式的通用实现
代码如下:
public class PrototypePattern {
public static void main(String[] args) {
ConcretePotrotypeA potrotypeA = new ConcretePotrotypeA();
potrotypeA.setDesc("ConcretePotrotypeA");
ConcretePotrotypeA clone = potrotypeA.clone();
clone.setDesc("clone");
System.out.println(potrotypeA);
System.out.println(clone);
}
}
/**
* 抽象原型
*
* @param <T>
*/
interface IPrototype<T> {
// 复制
T clone();
}
/**
* 具体原型
*/
class ConcretePotrotypeA implements IPrototype<ConcretePotrotypeA> {
private String desc;
@Override
public ConcretePotrotypeA clone() {
// 进行复制
ConcretePotrotypeA concretePotrotypeA = new ConcretePotrotypeA();
concretePotrotypeA.setDesc(this.getDesc());
return concretePotrotypeA;
}
public String getDesc() {
return desc;
}
public void setDesc(String desc) {
this.desc = desc;
}
@Override
public String toString() {
return "ConcretePotrotypeA{" + "desc='" + desc + '\'' + '}';
}
}
三、模式的应用实例
1、浅拷贝
Java内置了 Cloneable抽象原型接口
,自定义的类只需要实现该接口并重写 Object.clone()方法即可完成本类的复制。
一般使用 clone方法,需要满足几个条件:
- 对任何对象,都有 o.clone() != o。即克隆对象与原型对象不是同一个对象。
- 对任何对象,都有 o.clone().getClass() = o.getClass()。即克隆对象与原型对象的类型一样。
- 如果对象 o的 equals()方法定义恰当,则 o.clone().equals(o) 应当成立,可选条件。
代码如下;
public class ConcretePotrotypeB implements Cloneable{
private String desc;
private List<String> userList;
/**
* 浅拷贝
* @return
*/
@Override
protected ConcretePotrotypeB clone(){
ConcretePotrotypeB clone = null;
try {
/**
* super.clone()方法是基于内存二进制流的复制
*/
clone = (ConcretePotrotypeB) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
// get/set
}
测试:
public static void main(String[] args) {
ConcretePotrotypeB potrotypeB = new ConcretePotrotypeB();
potrotypeB.setDesc("ConcretePotrotypeB");
List<String> userList = new ArrayList<>();
userList.add("赵云");
userList.add("后裔");
potrotypeB.setUserList(userList);
ConcretePotrotypeB clone = potrotypeB.clone();
clone.setDesc("clone");
clone.getUserList().add("安琪拉");
System.out.println(potrotypeB);
System.out.println(clone);
System.out.println(clone == potrotypeB);
System.out.println(clone.getClass() == potrotypeB.getClass());
System.out.println(clone.equals(potrotypeB));
}
注意:
- 浅拷贝:如果类中存在引用对象属性,则原型对象与克隆对象的该属性会执行同一对象属性的引用。
- super.clone()方法:是直接从堆内存中以二进制流的方式进行复制,重新分配一个内存块,因此其效率很高。
2、深拷贝
针对引用对象属性的深拷贝, 手动给克隆对象的该属性分配另一块内存即可。在 Java中,如果想要完成原型对象的深克隆,通常使用序列化(Serializable )
的方式来完成。
代码如下;
public class ConcretePotrotypeC implements Cloneable, Serializable {
private String desc;
private List<String> userList;
/**
* 浅拷贝
*
* @return
*/
@Override
protected ConcretePotrotypeC clone() {
return deepClone();
}
/**
* 深拷贝
*
* @return
*/
public ConcretePotrotypeC deepClone() {
try {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (ConcretePotrotypeC) ois.readObject();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// get/set
}
测试:
public static void main(String[] args) {
ConcretePotrotypeC potrotypeC = new ConcretePotrotypeC();
potrotypeC.setDesc("ConcretePotrotypeC");
List<String> userList = new ArrayList<>();
userList.add("赵云");
userList.add("后裔");
potrotypeC.setUserList(userList);
ConcretePotrotypeC clone = potrotypeC.clone();
clone.setDesc("clone");
clone.getUserList().add("安琪拉");
System.out.println(potrotypeC);
System.out.println(clone);
System.out.println(clone == potrotypeC);
System.out.println(clone.getClass() == potrotypeC.getClass());
System.out.println(clone.equals(potrotypeC));
}
参考文章:
- 一文读懂浅拷贝和深拷贝区别:https://blog.51cto.com/u_14977428/2545103
– 求知若饥,虚心若愚。