快速理解原型模式,及其在源码中的应用
作者:鱼仔
博客首页: https://codeease.top
公众号:Java鱼仔
# (一)什么是原型模式
在正常的开发过程中,百分之99的对象实例都是通过new进行创建的,但是在实际的场景中,还是存在一些不通过new生成对象实例的应用场景。
比如:需要生成大量类似的对象实例时,如果都通过new去创建,性能并不好。
又比如:通过很复杂的方式生成了一个对象,现在要再创建出一个类似的对象出来。
上面的两种应用场景都可以通过“拷贝”这个动作来实现,这种根据一个已有的原型对象,拷贝生成新的对象的方式,就是设计模式中的原型模式(prototype)。
在Java中,可以通过Cloneable接口和clone方法很好地实现原型模式。
# (二)原型模式的简单例子
首先通过一个简单的例子让大家快速了解原型模式。
过年的时候公司给每个人发了一份邮件,这个邮件里除了署名和入职年份不一样以外,其他的内容都是相同的。比如:张三,这是你入职的第一年,祝你新年快乐XXXXX;李四,这是你入职的第二年,祝你新年快乐XXXXX。
这里的每一份邮件都可以理解为一个对象,而所有的对象实例都是相似的对象实例,因此就可以实用原型模式来拷贝生成这些重复的对象。
首先创建一个邮件对象,需要实现Cloneable接口:
public class Mail implements Cloneable{
private String name;
private String content;
private Integer years;
Mail(String name,String content,Integer years){
this.name = name;
this.content = content;
this.years = years;
System.out.println("对象创建成功");
}
private void showMail(){
System.out.println(name+",这是你入职的第"+years+"年,"+content);
}
@Override
protected Object clone() throws CloneNotSupportedException {
System.out.println("拷贝成功");
return super.clone();
}
public void setContent(String content) {
this.content = content;
}
public void setName(String name) {
this.name = name;
}
public void setYears(Integer years) {
this.years = years;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
接下来在使用的时候先通过new创建一个基础对象实例,其他的均通过clone进行拷贝:
public static void main(String[] args) throws CloneNotSupportedException {
Mail mail = new Mail("张三", "祝你新年快乐XXXXXX", 1);
mail.showMail();
Mail cloneMail = (Mail) mail.clone();
cloneMail.setName("李四");
cloneMail.setYears(2);
mail.showMail();
}
2
3
4
5
6
7
8
这样一个简单的原型模式就实现了。
# (三)基于管理器的原型模式
上面的例子可以增加对原型模式的理解,在实际开发中,更常用的是使用一个管理器去管理原型对象,可以在管理器类中通过一个HashMap保存原型对象。以水果店为例子。
首先定义了一个接口叫做Fruit,里面有两个方法:,shape方法用于展示水果的外观,createFruit方法用于通过克隆创建原型对象实例。
public interface Fruit extends Cloneable{
void shape();
Object createFruit() throws Exception;
}
2
3
4
接着定义管理器,管理器由HashMap进行管理,提供注册和创建实例的功能:
public class FruitManager {
private HashMap<String,Fruit> managerMap = new HashMap<>();
public void register(String name,Fruit fruit){
managerMap.put(name,fruit);
}
public Fruit create(String name) throws Exception{
Fruit fruit = managerMap.get(name);
return (Fruit) fruit.createFruit();
}
}
2
3
4
5
6
7
8
9
10
下一步就是定义两个具体的对象,需要注意的是createFruit()方法使用了clone()进行拷贝:
public class Apple implements Fruit{
@Override
public void shape() {
System.out.println("苹果是红色的圆形水果");
}
@Override
public Object createFruit() throws Exception {
return super.clone();
}
}
public class Orange implements Fruit{
@Override
public void shape() {
System.out.println("橘子是黄色的圆形水果");
}
@Override
public Object createFruit() throws Exception{
return super.clone();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
最后写一个测试方法:
public static void main(String[] args) throws Exception {
FruitManager manager = new FruitManager();
//注册到管理器中
manager.register("apple",new Apple());
manager.register("orange",new Orange());
//通过create方法不断拷贝新的对象
Fruit apple = manager.create("apple");
apple.shape();
Fruit orange = manager.create("orange");
orange.shape();
}
2
3
4
5
6
7
8
9
10
11
# (四)原型模式在源码中的应用
其实在很多地方都用到了原型模式,比如最常用的ArrayList,同样定义了clone方法供调用者进行拷贝。
抛去Cloneable相关的接口,一些对象之间的复制操作也是原型模式的实践,比如Spring里BeanUtils.copyProperties()这个方法,同样实现了根据一个已有的原型对象,拷贝生成新的对象。
# (五)总结
本章内容主要通过两个例子讲解了原型模式的概念以及应用,我是鱼仔,我们下期再见!