设计模式之创建型模式中的原型模式(Prototype Pattern)

Prototype Pattern

基本介绍

原型模式(Prototype Pattern)主要用于在保证性能的情况下创建重复的对象,也就是创建当前对象的克隆,这种模式是实现了一个原型接口。在开发过程中,如果我们已经明确了所需要创建对象的种类,且创建这种类型对象的代价比较大(创建数量庞大,频繁的数据库交互对数据库等),就可以用原型实例指定所要创建对象的种类,然后通过拷贝这些原型创建新的对象。这就有点像我们生物里面学的细胞分裂。

Prototype Pattern

日常举例

克隆羊的例子:有一只克隆羊叫多莉,没错就是这只羊,长的就是下面图片中的样子。。。现在按照这个样子再克隆N只多莉。。。
对,我是克隆羊多莉...

传统方式

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
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Sheep {
private String name;
private int age;
private String color;

public Sheep(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}

public Sheep() {
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getColor() {
return color;
}

public void setColor(String color) {
this.color = color;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* 以传统的简单方式克隆多莉羊
* @author sunys
*/
public class CloneDollySimple {
static class Client{
public static void main(String[] args) {
Sheep dolly = new Sheep("dolly",2,"gray");
Sheep sheep = new Sheep(dolly.getName(),dolly.getAge(),dolly.getColor());
Sheep sheep1 = new Sheep(dolly.getName(),dolly.getAge(),dolly.getColor());
Sheep sheepN = new Sheep(dolly.getName(),dolly.getAge(),dolly.getColor());
}
}
}

原型模式

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
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
public abstract class SheepPrototype implements Cloneable{
private String name;
private int age;
private String color;
private SheepPrototype mother;

public SheepPrototype(String name, int age, String color) {
this.name = name;
this.age = age;
this.color = color;
}

public SheepPrototype() {
}
protected abstract String eat();
public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

public String getColor() {
return color;
}

public void setColor(String color) {
this.color = color;
}
public SheepPrototype getMother() {
return mother;
}

public void setMother(SheepPrototype mother) {
this.mother = mother;
}
/**
* 克隆实例
* @return
*/
@Override
protected SheepPrototype clone() {
SheepPrototype sheepPrototype = null;
try {
sheepPrototype = (SheepPrototype) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return sheepPrototype;
}

@Override
public String toString() {
return "SheepPrototype{" +
"name='" + name + '\'' +
", age=" + age +
", color='" + color + '\'' +
", mother=" + mother +
'}';
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 创建当前对象的浅表副本
* @author sunys
*/
public class ConcreteSheepPrototype extends SheepPrototype{
public ConcreteSheepPrototype(String name, int age, String color) {
super(name, age, color);
}
@Override
protected String eat() {
return "Eating green grass";
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/**
*
* 以原型模式克隆多莉羊
* @author sunys
*/
public class CloneDollyPrototype {

static class Client{
public static void main(String[] args) {
ConcreteSheepPrototype dolly = new ConcreteSheepPrototype("dolly",2,"gray");
dolly.setMother(new ConcreteSheepPrototype("dolly",5,"gray"));
ConcreteSheepPrototype sheepPrototype = (ConcreteSheepPrototype) dolly.clone();
ConcreteSheepPrototype sheepPrototype1 = (ConcreteSheepPrototype) dolly.clone();
ConcreteSheepPrototype sheepPrototypeN = (ConcreteSheepPrototype) dolly.clone();
dolly.getMother().setColor("red");
dolly.setAge(6);
System.out.println(dolly);
System.out.println(sheepPrototype);
System.out.println(sheepPrototype1);
System.out.println(sheepPrototypeN);
}
}
}

运行结果:
浅拷贝

对比以上的两种方式很容易可以看出,传统的方式虽然易于理解,但是每次创建新的对象时,都需要重新初始化对象,并获取原始对象的属性,然后设置属性,显然,这种方式效率低;相对于传统方式,原型模式就方便快捷很多,在无需关注细节的情况下,就可以通过原型对象创建出另外的可定制对象,这种方式必须实现 Cloneable 接口。

原型模式主要包含3个角色:

  1. Prototype(抽象原型类):声明克隆方法的接口,是所有具体原型类的公共父类,它可是抽象类也可以是接口,甚至可以是具体实现类。
  2. ConcretePrototype(具体原型类):它实现抽象原型类中声明的克隆方法,在克隆方法中返回自己的一个克隆对象。
  3. Client(客户端):在客户类中,让一个原型对象克隆自身从而创建一个新的对象。

原型模式在Spring框架中的应用

1
2
3
4
5
6
7
<bean id="pointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut" scope="prototype">
<property name="patterns">
<list>
<value>com.syshlang.service..*Service.*(..)</value>
</list>
</property>
</bean>
1
2
3
4
5
6
7
@Bean
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE )
public class Sheep {
private String name;
private int age;
private String color;
}

在上面的配置中可以看到一个标签属性scope=“prototype”,原型模式与名称为prototype的作用域相似。在Spring中,一个类如果被标记为”prototype”,那么将该类注入到另一个bean中或者调用容器的getBean()方法时,都会产生一个新的bean实例。其实,这个产生新的bean的过程就是利用了原型模式。

源码:org.springframework.beans.factory.support.AbstractBeanFactory#getBean(java.lang.String)

1
2
3
4
5
6
7
//---------------------------------------------------------------------
// Implementation of BeanFactory interface
//---------------------------------------------------------------------

public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}

源码:org.springframework.beans.factory.support.AbstractBeanFactory#doGetBean()(片段)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
...
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
}
...

附:本次演示的项目地址
https://github.com/syshlang/java-design-patterns