软件设计模式白话文系列(三)原型模式
软件设计模式白话文系列(三)原型模式
1、描述
用原型实例指定创建对象的种类,并且通过拷贝这些原型对象的属性来创建新的对象。通俗点的意思就是一个对象无需知道任何创建细节就可以创建出另外一个可定制的对象。可以简单看作为复制、粘贴操作。
原型模式的克隆分为浅克隆和深克隆。
- 浅克隆
克隆对象的属性和原对象完全相同,基本类型的属性属于值传递,改变一个对象的值,另一个不会受影响。对于引用数据类型的属性,仍指向原有属性所指向的对象的内存地址。引用类型是传递引用,指向同一片内存空间,改变一个对象的引用类型的值,另一个对象也会随之改变。但是这里需要注意 String 类型却是一个特殊,String虽然属于引用类型,但是 String 类是不可改变的,它是一个常量,一个对象调用 clone 方法,克隆出一个新对象,这时候两个对象的同一个 String 类型的属性是指向同一片内存空间的,但是如果改变了其中一个,会产生一片新的内存空间,此时该对象的这个属性的引用将指向这片新的内存空间,此时两个对象的String类型的属性指向的就是不同的2片内存空间,改变一个不会影响到另一个,可以当做基本类型来使用。
- 深克隆
克隆对象的所有属性都会被克隆,不再指向原有对象地址。
2、适用性
- 对象之间相同或相似,即只是个别的⼏个属性不同的时候。
- 创建对象成本较⼤,例如初始化时间⻓,占⽤CPU太多,或者占⽤⽹络资源太多等,需要优化资 源。
- 创建⼀个对象需要繁琐的数据准备或访问权限等,需要提⾼性能或者提⾼安全性。
- 系统中⼤量使⽤该类对象,且各个调⽤者都需要给它的属性重新赋值。
该设计模式使用场景很广泛,日常开发中难免有操作某个对象时又不想影响原有对象的情况,具体还需要看业务需求使用浅克隆或者是深克隆。
3、实现逻辑
- 抽象原型类:规定了具体原型对象必须实现的的 clone() 方法。
- 具体原型类:实现抽象原型类的 clone() 方法,它是可被复制的对象。
- 访问类:使用具体原型类中的 clone() 方法来复制新的对象。
Java 中通过 Object 类中提供的 clone()
方法和 Cloneable
接口来实现浅克隆,clone() 方法由 native
关键字修饰,通过本地方法拷贝地址值(基于内存二进制流的复制)来实现,不但效率高还免去我们手动实现的烦恼。 Cloneable 接口充当抽象原型类,而我们实现了 Cloneable 接口的子实现类就是具体的原型类。
需要注意由于 Object 本身没有实现 Cloneable 接口,所以不重写 clone 方法并且进行调用的话会发生 CloneNotSupportedException 异常。
4、实战代码
4.1 浅克隆
/**
* 原型类引用类
*
* @author Eajur.Wen
* @version 1.0
* @date 2022-11-05 15:29:57
*/
@Setter
@Getter
@ToString
public class Account {
private String name;
}
/**
* 具体的原型类
*
* @author Eajur.Wen
* @version 1.0
* @date 2022-10-28 17:00:11
*/
@Getter
@Setter
@ToString
public class Member implements Cloneable {
private int id;
private String name;
private Account account;
@Override
protected Member clone() throws CloneNotSupportedException {
return (Member) super.clone();
}
}
/**
* 测试类
*
* @author Eajur.Wen
* @version 1.0
* @date 2022-11-05 15:34:36
*/
public class ShallowClone {
public static void main(String[] args) throws CloneNotSupportedException {
Account account = new Account();
account.setName("account");
Member member = new Member();
member.setId(1);
member.setName("member");
member.setAccount(account);
Member cloneMember = member.clone();
// clone 对象与原对象的比较
System.out.println(cloneMember == member);
System.out.println(cloneMember.getId() == member.getId());
System.out.println(cloneMember.getAccount() == member.getAccount());
System.out.println(cloneMember.getName() == member.getName());
// 修改 clone 对象属性
account.setName("newAccount");
cloneMember.setId(2);
cloneMember.setName("newMember");
// 查看原型对象
System.out.println(member);
System.out.println(cloneMember);
}
}
执行结果:
从结果不难看出,我们得到的 clone 对象是一个新的对象,但是属性的值或者引用地址都是一样的。再修改 clone 对象后,基本类型的属性的值不会跟着修改, String 类型的属性由于自身特性指向了新的地址值,而我们 Account 类的属性,在 clone 对象的值被修改,两个对象的 account 属性都指向同一个内存地址值,所以会被一起修改。我在生产开发中需要注意原型模式对象引用属性谨慎操作。
4.2 对象流深克隆
进行深克隆需要使用对象流。不用实现 Cloneable 接口,注意实现序列化接口 Serializable
/**
* 原型类引用类
*
* @author Eajur.Wen
* @version 1.0
* @date 2022-11-05 15:29:57
*/
@Setter
@Getter
@ToString
public class Account implements Serializable {
private String name;
}
/**
* 具体的原型类
*
* @author Eajur.Wen
* @version 1.0
* @date 2022-10-28 17:00:11
*/
@Getter
@Setter
@ToString
public class Member implements Serializable {
private int id;
private String name;
private Account account;
}
/**
* 深克隆测试类
*
* @author Eajur.Wen
* @version 1.0
* @date 2022-11-05 16:26:57
*/
public class DeepClone {
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
Account account = new Account();
account.setName("account");
Member member = new Member();
member.setId(1);
member.setName("member");
member.setAccount(account);
// 创建对象输出流对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("XXX/member.txt"));
// 将 member 对象写出到文件中
oos.writeObject(member);
oos.close();
// 创建对象出入流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("XXX/member.txt"));
// 读取对象
Member cloneMember = (Member) ois.readObject();
// 修改引用对象
Account newAccount = cloneMember.getAccount();
newAccount.setName("newAccount");
// 查看原型对象
System.out.println(member);
System.out.println(cloneMember);
}
}
执行结果:
通过序列化和反序列化得到的对象地址值不同来达到目的
4.3 JSON 深克隆
基于 Json 实现对象深度 Clone,不再需要实现 Clonable 接⼝,但是需要引用 JSON 工具包,这里使用 hutool-json
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-json</artifactId>
<version>5.8.9</version>
</dependency>
测试代码:
/**
* JSON 方式深克隆测试类
*
* @author Eajur.Wen
* @version 1.0
* @date 2022-11-07 19:21:14
*/
public class JsonDeepClone {
public static void main(String[] args) {
Account account = new Account();
account.setName("account");
Member member = new Member();
member.setId(1);
member.setName("member");
member.setAccount(account);
//将 member 对象写到 JSON 字符串
String json = JSONUtil.toJsonStr(member);
//json 字符串转换为 member对象
Member cloneMember = (Member) JSONUtil.toBean(json, Member.class);
//获取c2奖状所属学生对象
Account newAccount = cloneMember.getAccount();
newAccount.setName("newAccount");
// 查看原型对象
System.out.println(member);
System.out.println(cloneMember);
}
}
执行结果:
可以使⽤深克隆⽅式保存对象的状态,使⽤原型模式将对象复制⼀份,并将其状态保存起来,简化了 创建对象的过程,以便在需要的时候使⽤(例如恢复到历史某⼀状态),可辅助实现撤销操作。
作者:eajur
出处:https://www.cnblogs.com/eajur/p/16860554.html
版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】