✨这里是第七人格的博客✨小七,欢迎您的到来~✨
🍅系列专栏:设计模式🍅
✈️本篇内容: 原型模式✈️
🍱本篇收录完整代码地址:https://gitee.com/diqirenge/design-pattern🍱
楔子
原型模式是一种创建型设计模式,它允许复制现有对象来创建新对象,而不是通过实例化新对象,这样可以在创建复杂对象时,节约时间。如果你经常使用 JavaScript,那么原型模式是一种比较常用的开发模式,原型链也是面试经常被问到的问题。但是如果你是使用Java的后端程序员,那么原型模式的确是很少在业务中运用的。
需求背景
我们从远程RPC获取了一份数据,假设为数据A,在不影响数A的情况下,我们需要从数据A复制一份数据B,然后在数据B上进行操作。
分析设计
因为是将数据A复制到数据B,所以可以实现Cloneable,使用原型模式。
又因为不能影响数据A,所以我们需要考虑使用浅拷贝还是深拷贝。
怎么理解浅拷贝和深拷贝呢?
浅拷贝是按位拷贝对象,会创建一个新对象,但不会修改原对象的值。它复制的是对象的引用地址,没有开辟新的栈,所以复制的结果是两个对象指向同一个地址。因此,当修改其中一个对象的属性时,另一个对象的属性也会跟着改变。
深拷贝是复制对象本身,不共享内存。这意味着它会创建一个新的对象,并且复制原对象的所有字段以及字段所指向的动态分配内存。因此,修改新对象不会影响原对象。
深拷贝相比于浅拷贝速度较慢并且花销较大,因为它需要复制更多的数据。
举个例子,浅拷贝就好比钢铁侠的机甲,不管他制造了多少套,但是他们的AI都是贾维斯,都是一个。
而深拷贝,就是一套机甲一个AI。
所以我们上面的需求,需要使用深拷贝。
UML图
根据分析设计,我们可以先画一个简单的UML图,后面通过UML图编码
模块名称
prototype
模块地址
模块描述
原型模式代码示例
浅拷贝
代码实现
1、首先我们实现浅拷贝
/**
* 原型模式
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/21
*/
public class RpcData implements Cloneable {
private String name;
private String age;
private Pet pet;
public static class Pet {
private String name;
private String age;
private String tyep;
public Pet() {
}
public Pet(String name, String age, String tyep) {
this.name = name;
this.age = age;
this.tyep = tyep;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getTyep() {
return tyep;
}
public void setTyep(String tyep) {
this.tyep = tyep;
}
@Override
public String toString() {
System.out.println("Pet{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", tyep='" + tyep + '\'' +
'}');
return super.toString();
}
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public Pet getPet() {
return pet;
}
public void setPet(Pet pet) {
this.pet = pet;
}
@Override
public String toString() {
System.out.println("RpcData{" +
"name='" + name + '\'' +
", age='" + age + '\'' +
", pet=" + pet +
'}');
return super.toString();
}
}
2、编写测试方法
/**
* 原型模式
* 关注公众号【奔跑的码畜】,一起进步不迷路
*
* @author 第七人格
* @date 2023/11/21
*/
public class RpcDataPrototypeTest {
@Test
public void test_shallow_copy() throws CloneNotSupportedException {
System.out.println("==========浅拷贝开始==========");
RpcData rpcData = new RpcData();
Pet pet = new Pet("小黑", "1", "dog");
rpcData.setName("第七人格");
rpcData.setAge("18");
rpcData.setPet(pet);
RpcData cloneData = (RpcData) rpcData.clone();
Pet clonePet = cloneData.getPet();
System.out.println("原型对象和克隆对象是否是同一个对象:" + (rpcData == cloneData));
System.out.println("原型对象和克隆对象中的宠物是否是同一个对象:" + (pet == clonePet));
System.out.println("==========浅拷贝结束==========");
}
}
3、测试结果
①执行test_shallow_copy方法
==浅拷贝开始==
原型对象和克隆对象是否是同一个对象:false
原型对象和克隆对象中的宠物是否是同一个对象:true
==浅拷贝结束==
实现要点
- 实现 Cloneable 接口,重写clone方法
深拷贝
一般有两种实现方式:
①先将对象序列化,然后再反序列化成新的对象
②递归拷贝对象、对象的引用对象以及引用对象的引用对象,直到对象属性中只有基本数据类型数据为止
我们这里只演示第一种方式
代码实现
1、RpcData和Pet实现Serializable接口
2、添加深拷贝-序列化方法
public Object deepCopy(Object object) throws IOException, ClassNotFoundException {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(object);
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return oi.readObject();
}
3、编写测试方法
@Test
public void test_deepCopy_copy() throws IOException, ClassNotFoundException {
System.out.println("==========深拷贝开始==========");
RpcData rpcData = new RpcData();
Pet pet = new Pet("小黑", "1", "dog");
rpcData.setName("第七人格");
rpcData.setAge("18");
rpcData.setPet(pet);
RpcData cloneData = (RpcData) rpcData.deepCopy(rpcData);
Pet clonePet = cloneData.getPet();
System.out.println("原型对象和克隆对象是否是同一个对象:" + (rpcData == cloneData));
System.out.println("原型对象和克隆对象中的宠物是否是同一个对象:" + (pet == clonePet));
System.out.println("===========深拷贝结束==========");
}
4、测试结果
==深拷贝开始==
原型对象和克隆对象是否是同一个对象:false
原型对象和克隆对象中的宠物是否是同一个对象:false
=深拷贝结束
实现要点
①先将对象序列化,然后再反序列化成新的对象
总结
原型模式的原理和实现都是比较简单的,主要是需要理解浅拷贝和深拷贝的原理。有机会还是要用在项目中,多实践才能掌握得更好。
面对对象面对君,不负代码不负卿