设计模式(十一)—— 原型模式

原型模式

1、定义与特点

定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。原型实例指定了要创建的对象的种类。用这种方式创建对象非常高效,根本无须知道对象创建的细节。

原型模式的核心是一个clone方法,通过该方法进行对象的拷贝,Java提供Cloneable接口来标示这个对象是可拷贝的。Cloneable接口只是起到标示作用,具体实现需要我们重写clone()方法。

在Object对象中提供了一个clone()方法,我们需要重写此方法。

2、原型模式的优点

  • 性能优良:原型模式是在内存二进制流的拷贝,比直接new一个对象要好。
  • 逃避构造函数的约束:这是双刃剑,既是优点也是缺点,直接在内存拷贝,不会执行构造函数方法。
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
public class EmailTemplate implements Cloneable{

private String sendTo;

private String message;

private String subject;

private String from;
// 省略get,set方法
@Override
protected EmailTemplate clone() throws CloneNotSupportedException {
return (EmailTemplate) super.clone();
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException{
EmailTemplate template = new EmailTemplate();
template.setFrom("xiaosen");
template.setMessage("这是原型模式复制消息");
template.setSubject("原型模式测试");
EmailTemplate[] templateArray = new EmailTemplate[3];
for (int i=0; i<templateArray.length; i++){
templateArray[i] = template.clone();
templateArray[i].setSendTo("接受者"+(i+1));
}
Stream.of(templateArray).forEach(emailTemplate -> {
System.out.println(emailTemplate.getSendTo()+" 接收到["+emailTemplate.getFrom()+"]的消息,主题:"+emailTemplate.getSubject()+"。消息:"+emailTemplate.getMessage());
});
}
}

测试输出:

1
2
3
接受者1 接收到[xiaosen]的消息,主题:原型模式测试。消息:这是原型模式复制消息
接受者2 接收到[xiaosen]的消息,主题:原型模式测试。消息:这是原型模式复制消息
接受者3 接收到[xiaosen]的消息,主题:原型模式测试。消息:这是原型模式复制消息

3、使用场景

  • 资源优化类场景:类初始化需要的非常多的资源,包括数据和硬件。
  • 性能和安全要求的场景;
  • 一个对象多个修改者的场景;

在实际项目中,原型模式通常是和工厂模式一起使用,通过clone()方法创建一个对象。

4、注意事项

1、构造函数不会被执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* @author liuguisen
* @date 2019/4/19 17:43
* @description
*/
public class ConstructorTest implements Cloneable {
public ConstructorTest() {
System.out.println("构造器执行。。。");
}

@Override
protected ConstructorTest clone() throws CloneNotSupportedException {
return (ConstructorTest) super.clone();
}

public static void main(String[] args) throws CloneNotSupportedException{
ConstructorTest constructorTest = new ConstructorTest();
ConstructorTest constructorTest1 = constructorTest.clone();
}
}
// 输出
// 构造器执行。。。

2、浅拷贝和深拷贝

先看一个浅拷贝的例子:

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
import java.util.ArrayList;
import java.util.List;

/**
* @author liuguisen
* @date 2019/4/19 17:50
* @description
*/
public class ShallowClone implements Cloneable {
private List<String> list = new ArrayList<>();

public List<String> getList() {
return list;
}

public void setList(String value) {
this.list.add(value);
}

@Override
protected ShallowClone clone() throws CloneNotSupportedException {
return (ShallowClone) super.clone();
}

public static void main(String[] args) throws CloneNotSupportedException{
ShallowClone shallowClone = new ShallowClone();
shallowClone.setList("xiaosen");
System.out.println(shallowClone.getList());
ShallowClone shallowClone1 = shallowClone.clone();
shallowClone1.setList("lalala");
System.out.println(shallowClone.getList());
System.out.println(shallowClone1.getList());
}
}

Object类提供的clone方法只会拷贝本对象,对象内的数组、引用对象不会拷贝,还是指向原生对象的内部元素地址,这种拷贝叫做浅拷贝。

注意:使用原型模式时,引用的成员变量必须满足两个条件才不会被拷贝:一是类的成员变量,而不是方法内的变量;二是必须是一个可变的。

下面是修改的深拷贝:

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
import java.util.ArrayList;
import java.util.List;

/**
* @author liuguisen
* @date 2019/4/19 18:06
* @description
*/
public class DeepCopy implements Cloneable {
private ArrayList<String> list = new ArrayList<>();

public List<String> getList() {
return list;
}

public void setList(String value) {
this.list.add(value);
}

@Override
protected DeepCopy clone() throws CloneNotSupportedException {
DeepCopy deepCopy = (DeepCopy) super.clone();
deepCopy.list = (ArrayList<String>) this.list.clone();
return deepCopy;
}

public static void main(String[] args) throws CloneNotSupportedException{
DeepCopy deepCopy = new DeepCopy();
deepCopy.setList("xiaosen");
System.out.println(deepCopy.getList());
DeepCopy deepCopy1 = deepCopy.clone();
deepCopy1.setList("lalala");
System.out.println(deepCopy.getList());
System.out.println(deepCopy1.getList());
}
}

深拷贝还有一种通过自己写二进制流来操作对象,然后实现对象的深拷贝。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static <T> List<T> depCopy(List<T> srcList) {
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
try {
ObjectOutputStream out = new ObjectOutputStream(byteOut);
out.writeObject(srcList);
ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
ObjectInputStream inStream = new ObjectInputStream(byteIn);
List<T> destList = (List<T>) inStream.readObject();
return destList;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}

本文代码GitHub


欢迎关注公众号:
公众号微信

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
显示 Gitment 评论