原型模式
# 原型模式
# 定义
原型模式是一种创建型设计模式,其目的是通过复制现有对象来创建新对象,而不是通过实例化类来创建对象。它允许我们通过克隆(或复制)现有对象来创建新对象,从而避免了使用常规的实例化方法。
原型模式是原型对象的复制。可以通过浅拷贝或深拷贝来实现复制操作。浅拷贝只复制对象的基本数据类型和引用,而深拷贝会递归地复制对象的所有属性和关联对象。
深拷贝和浅拷贝
浅拷贝:创建一个新对象,将基本数据类型如int、float、String等,直接复制字段值到新的对象中,引用类型数据复制引用对象的地址到新对象中,新对象和原始对象共享同一个引用对象,其中一个修改会影响到另一个对象。
深拷贝:创建一个新对象,将基本数据类型和引用类型都完全复制一份到新对象中,深拷贝会递归复制引用类型字段指向的对象,新对象和旧对象的引用对象完全独立、互不影响。
# 结构
抽象原型类:可以是接口或基类,声明clone方法,在简单情况下可以不用抽象原型类,直接用具体原型类就可以。
具体原型类:实现或扩展clone方法,返回基类的抽象原型对象。
- img: https://bitouyun.com/images/design-pattern/prototype.png
link: https://bitouyun.com/images/design-pattern/prototype.png
name: 原型模式
1
2
3
2
3
# 优点
原型模式通过复制现有对象来创建新对象,减少了对象的创建成本,提高了对象的创建速度,并且具有灵活性和封装性。
# 缺点
浅拷贝和深拷贝,一旦使用不正确,就会造成bug。如果系统中存在多个具体原型类,每个类都需要实现克隆方法,这可能会导致代码的重复。
# 应用场景
当需要创建的对象的成本较高时,如:使用new生成一个对象需要非常繁琐的过程(依赖外部资源、数据准备、访问权限等)、构造函数比较复杂、在循环中产生大量对象等,可以使用原型模式。
# 示例代码
场景:
克隆对象:深拷贝和浅拷贝
/**
* 原型接口
* 简单场景可以不用原型接口,直接使用具体类
*/
public interface Prototype {
Prototype clone();
}
1
2
3
4
5
6
7
2
3
4
5
6
7
/**
* 具体需要克隆的类
* 浅克隆(拷贝),基本数据类型复制字段值,引用类型对象复制地址
*/
@Data
public class ConcretePrototype implements Prototype {
private String name;
private int age;
private List<String> hobbies;
@Override
public Prototype clone() {
ConcretePrototype concretePrototype = new ConcretePrototype();
concretePrototype.setName(this.name);
concretePrototype.setAge(this.age);
concretePrototype.setHobbies(this.hobbies);
return concretePrototype;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* 测试类
*/
@Slf4j
public class Client {
public static void main(String[] args) {
// 创建原始对象
ConcretePrototype concretePrototype = new ConcretePrototype();
concretePrototype.setName("小明");
concretePrototype.setAge(8);
ArrayList<String> hobbies = new ArrayList<>();
hobbies.add("游泳");
hobbies.add("画画");
concretePrototype.setHobbies(hobbies);
log.info("原始对象:{}", concretePrototype);
// 克隆新对象 - 浅拷贝
ConcretePrototype concretePrototypeClone = (ConcretePrototype) concretePrototype.clone();
log.info("原始对象的爱好:{}", concretePrototype.getHobbies());
log.info("新对象的爱好:{}", concretePrototypeClone.getHobbies());
// true 浅克隆,对象地址相同
log.info("原始对象和新对象引用对象地址是否相同:{}", concretePrototype.getHobbies() == concretePrototypeClone.getHobbies());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 具体需要克隆的类
* 深克隆(拷贝),基本类型和引用类型都会完全复制一份。
*/
@Data
public class ConcretePrototypeDeep implements Serializable {
private String name;
private int age;
private List<String> hobbies;
@SneakyThrows
@Override
public Object clone() {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
// 写入当前对象
oos.writeObject(this);
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return ois.readObject();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 测试类
*/
@Slf4j
public class Client2 {
public static void main(String[] args) {
// 创建原始对象
ConcretePrototypeDeep concretePrototypeDeep = new ConcretePrototypeDeep();
concretePrototypeDeep.setName("小明");
concretePrototypeDeep.setAge(8);
ArrayList<String> hobbies = new ArrayList<>();
hobbies.add("游泳");
hobbies.add("画画");
concretePrototypeDeep.setHobbies(hobbies);
log.info("新对象:{}", concretePrototypeDeep);
// 克隆新对象 - 深拷贝
ConcretePrototypeDeep concretePrototypeClone = (ConcretePrototypeDeep) concretePrototypeDeep.clone();
log.info("原始对象的爱好:{}", concretePrototypeDeep.getHobbies());
log.info("新对象的爱好:{}", concretePrototypeClone.getHobbies());
// false 深拷贝,对象地址不相同
log.info("原始对象和新对象引用地址是否相同:{}", concretePrototypeDeep.getHobbies() == concretePrototypeClone.getHobbies());
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// Make sure to add code blocks to your code group
上次更新: 2023/11/17, 09:38:49