23种设计模式之原型模式
文章目录
参考资料
- Java设计模式:23种设计模式全面解析(超级详细)
- 韩顺平老师的Java设计模式(图解+框架源码剖析)
- 秦小波老师的《设计模式之禅》
下文如有错漏之处,敬请指正
一、简介
定义
通过克隆原型的实例对象,创建新的对象。
原理
原型实例实现Cloneable接口(来标示该对象是可克隆的)并重写clone()方法,通过 原型对象.clone() 方法创建新对象。
特点
-
原型模式是一种创建型设计模式
-
使用克隆创建的对象不会调用构造函数
-
对于重量级的对象使用clone比new效率更高
优点
-
性能优良
原型模式是基于内存二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量的对象时,原型模式可以更好地体现其优点。
缺点
-
逃避构造函数的约束
由于原型模式是直接在内存中拷贝,因此构造函数不会被执行,减少了约束,可以通过原型模式破坏单例模式。
通用类图
- Client:客户端对象,通过Prototype对象的clone方法,创建新的对象
- Prototype:原型对象,实现Cloneable接口进行克隆标示并重写clone()方法
应用场景
-
资源优化场景
创建新对象成本较大,如类初始化需要占用较长的时间、占用太多的CPU资源或网络资源,新的对象可以使用原型模式获得。
-
性能和安全要求的场景
通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
-
保存对象状态
如果系统要保存对象的状态,而对象的状态变化很小,或者对象本身占用内存较少时,可以使用原型模式配合备忘录模式来实现。
-
1个对象对多个修改者
一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以考虑使用原型模式拷贝多个对象供调用者使用。
在实际项目中,原型模式很少单独出现,一般是和工厂方法模式一起出现,通过clone的 方法创建一个对象,然后由工厂方法提供给调用者。
二、实现分类
浅克隆(浅拷贝)
-
通过实现Cloneable接口进行克隆标示并重写clone()方法可以实现浅克隆。
-
在浅克隆中,如果原型对象的属性是值类型,会复制其值;如果属性是引用类型,将复制其引用地址。
-
注意:
因为String类型是不可变数据类型,当它的值改变时,它将引用新的值的地址;原引用地址如果有对象引用就保留,否则会被回收。
如果原型对象修改了String对象,不会影响克隆对象的String对象;反之,克隆对象修改了String对象,也不会影响原型对象的String对象。String字符串详解
(在此可以将String类型当做值类型)
深克隆(深拷贝)
-
通过重写clone()方法或对象序列化(Serialization)等方式来实现深克隆。
-
在深克隆中,无论原型对象的属性是值类型还是引用类型,都将复制一份给克隆对象。
-
注意:
-
如果使用重写clone()方法实现深克隆,被克隆的对象需要实现Cloneable接口;
-
如果使用序列化实现深克隆,被克隆的对象要实现Serializable接口。
-
浅克隆原型模式
需求:
一个牧羊人可以牧养多只羊,每只羊都大同小异,每只羊只有一个主人。
Prototype:Owner(牧羊人)、Sheep(羊)
Sheep:
package prototype.shallow;
public class Sheep implements Cloneable {
private String name;
public Sheep() {
}
public Sheep(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
'}';
}
}
Owner:
package prototype.shallow;
import java.util.ArrayList;
import java.util.List;
public class Owner implements Cloneable {
private String name;
private List<Sheep> sheepList = new ArrayList<>();
public Owner() {
}
public Owner(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Sheep> getSheepName() {
return sheepList;
}
public void setSheepName(Sheep sheep) {
this.sheepList.add(sheep);
}
@Override
public String toString() {
return "Owner{" +
"name='" + name + '\'' +
", sheepList=" + sheepList +
'}';
}
}
Client:
package prototype.shallow;
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
// 原型实例
Owner owner = new Owner("Space");
owner.setSheepName(new Sheep("肖恩1号"));
// 克隆对象
Owner clone = (Owner) owner.clone();
clone.setName("Qi");
clone.setSheepName(new Sheep("肖恩2号"));
owner.setSheepName(new Sheep("肖恩3号"));
System.out.println(owner);
System.out.println(clone);
/**
* 输出结果:
* Owner{name='Space', sheepList=[Sheep{name='肖恩1号'}, Sheep{name='肖恩2号'}, Sheep{name='肖恩3号'}]}
* Owner{name='Qi', sheepList=[Sheep{name='肖恩1号'}, Sheep{name='肖恩2号'}, Sheep{name='肖恩3号'}]}
* 因为是浅克隆,原型实例内部的引用类型的属性只会拷贝引用地址,因此两个Owner的sheepList的值是一样的
*/
}
}
深克隆原型模式
1. 重写clone()方法
需求:
一个牧羊人可以牧养多只羊,每只羊都大同小异,每只羊只有一个主人。
Prototype:Owner(牧羊人)、Sheep(羊)
Sheep:
package prototype.deep.Cloneable;
public class Sheep implements Cloneable{
private String name;
public Sheep() {
}
public Sheep(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
'}';
}
}
Owner:
package prototype.deep.Cloneable;
import java.util.ArrayList;
import java.util.List;
public class Owner implements Cloneable {
private String name;
private ArrayList<Sheep> sheepList = new ArrayList<>();
public Owner() {
}
public Owner(String name) {
this.name = name;
}
@Override
protected Owner clone() throws CloneNotSupportedException {
// 对值类型的克隆
Owner deep = (Owner) super.clone();
// 对引用类型的克隆
List<Sheep> sheepList = new ArrayList<>();
deep.sheepList = (ArrayList<Sheep>) this.sheepList.clone();
//返回克隆的对象
return deep;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Sheep> getSheepName() {
return sheepList;
}
public void setSheepName(Sheep sheep) {
this.sheepList.add(sheep);
}
@Override
public String toString() {
return "Owner{" +
"name='" + name + '\'' +
", sheepList=" + sheepList +
'}';
}
}
Client:
package prototype.deep.Cloneable;
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
// 原型实例
Owner owner = new Owner("Space");
owner.setSheepName(new Sheep("肖恩1号"));
// 克隆对象
Owner clone = owner.clone();
clone.setName("Qi");
clone.setSheepName(new Sheep("肖恩2号"));
owner.setSheepName(new Sheep("肖恩3号"));
System.out.println(owner);
System.out.println(clone);
/**
* 输出结果:
* Owner{name='Space', sheepList=[Sheep{name='肖恩1号'}, Sheep{name='肖恩3号'}]}
* Owner{name='Qi', sheepList=[Sheep{name='肖恩1号'}, Sheep{name='肖恩2号'}]}
*/
}
}
2. 使用对象的序列化
需求:
一个牧羊人可以牧养多只羊,每只羊都大同小异,每只羊只有一个主人。
Prototype:Owner(牧羊人)、Sheep(羊)
Sheep:
package prototype.deep.serilizable;
import java.io.*;
public class Sheep implements Serializable {
private String name;
public Sheep() {
}
public Sheep(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
'}';
}
}
Owner:
package prototype.deep.serilizable;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class Owner implements Serializable {
private String name;
private List<Sheep> sheepArrayList = new ArrayList<>();
public Owner() {
}
public Owner(String name) {
this.name = name;
}
protected Object deepClone() throws Exception {
//将对象写入流中
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(this);
//将对象从流中取出
ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (ois.readObject());
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List<Sheep> getSheepName() {
return sheepArrayList;
}
public void setSheepName(Sheep sheep) {
this.sheepArrayList.add(sheep);
}
@Override
public String toString() {
return "Owner{" +
"name='" + name + '\'' +
", sheepList=" + sheepArrayList +
'}';
}
}
Client:
package prototype.deep.serilizable;
public class Client {
public static void main(String[] args) throws Exception {
// 原型实例
Owner owner = new Owner("Space");
owner.setSheepName(new Sheep("肖恩1号"));
// 克隆对象
Owner clone = (Owner) owner.deepClone();
clone.setName("Qi");
clone.setSheepName(new Sheep("肖恩2号"));
owner.setSheepName(new Sheep("肖恩3号"));
System.out.println(owner);
System.out.println(clone);
/**
* 输出结果:
* Owner{name='Space', sheepList=[Sheep{name='肖恩1号'}, Sheep{name='肖恩3号'}]}
* Owner{name='Qi', sheepList=[Sheep{name='肖恩1号'}, Sheep{name='肖恩2号'}]}
*/
}
}
三、总结
- 原型模式可以通过浅克隆和深克隆两种方式实现
- 原型模式先产生出一个包含大量共有信息的类,然后拷贝出副本,修正细节信息, 建立了一个完整的个性对象。