Johuer's Blog

多学知识,精简代码

0%

图解设计模式(Creational)-Prototype 4/23

通过复制生成实例

适用条件

(1) 对象种类繁多,无法将他们整合到一个类中

(2) 难以根据类生成实例
生成实例的过程过于复杂,很难根据类来生成实例

(3) 想解耦框架与生成的实例
生成实例的框架不依赖具体类,不能通过类名来生成实例,可以实现“注册”一个原型实例

参与角色

  • Prototype:定义用于复制现有实例来生成新实例的方法
  • ConcretePrototype:实现复制现有实例并生成新实例的方法
  • Client

UML

logo

示例代码

发邮件为例子,假如需要给多人发邮件,邮件内容一样,接收人不一致。

Mail

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Data
public class Mail implements Cloneable {

private String content;
private String receiver;

public Mail(String content) {
this.content = content;
}

@Override
protected Mail clone() {
Mail mail = null;
try {
mail = (Mail) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return mail;
}
}

Main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Main {
public static void main(String[] args) {
Mail mail = new Mail("邮件内容都是一样的");

for (int i = 0; i < 10; i++) {
mail.setReceiver("user" + i);
sendMail(mail);
}
}

public static void sendMail(Mail mail) {
String msg = String.format("内容:%s 收件人%s", mail.getContent(), mail.getReceiver());
System.out.println(msg);
}
}

运行结果如下:

1
2
3
4
5
6
7
8
9
10
内容:邮件内容都是一样的  收件人user0
内容:邮件内容都是一样的 收件人user1
内容:邮件内容都是一样的 收件人user2
内容:邮件内容都是一样的 收件人user3
内容:邮件内容都是一样的 收件人user4
内容:邮件内容都是一样的 收件人user5
内容:邮件内容都是一样的 收件人user6
内容:邮件内容都是一样的 收件人user7
内容:邮件内容都是一样的 收件人user8
内容:邮件内容都是一样的 收件人user9

这是一个单线程没有问题,假如发送一次时间0.02s, 600万封邮件需要33小时,也就是一整天也发不完,ok我们把sendMail方法更改为多线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Main {
public static void main(String[] args) {
Mail mail = new Mail("邮件内容都是一样的");

for (int i = 0; i < 10; i++) {
mail.setReceiver("user" + i);
sendMailThread(mail);
}
}

public static void sendMail(Mail mail) {
String msg = String.format("内容:%s 收件人%s", mail.getContent(), mail.getReceiver());
System.out.println(msg);
}

public static void sendMailThread(Mail mail) {
new Thread(() -> sendMail(mail)).start();
}
}

运行结果如下:

1
2
3
4
5
6
7
8
9
10
内容:邮件内容都是一样的  收件人user2
内容:邮件内容都是一样的 收件人user2
内容:邮件内容都是一样的 收件人user5
内容:邮件内容都是一样的 收件人user6
内容:邮件内容都是一样的 收件人user3
内容:邮件内容都是一样的 收件人user9
内容:邮件内容都是一样的 收件人user7
内容:邮件内容都是一样的 收件人user9
内容:邮件内容都是一样的 收件人user8
内容:邮件内容都是一样的 收件人user5

问题出现了,当上一个线程还没有发送完成,下一个线程直接就把接收人更改了,线程不安全了(当然你有N种方法解决线程安全),但是我们这里用Prototype设计模式来解决。

原型模式是在内存二进制流的拷贝,要比直接new一个对象性能好很多

1
2
3
4
5
for (int i = 0; i < 10; i++) {
Mail cloneMail = mail.clone();
cloneMail.setReceiver("user" + i);
sendMailThread(cloneMail);
}

云行结果如下:

1
2
3
4
5
6
7
8
9
10
内容:邮件内容都是一样的  收件人user7
内容:邮件内容都是一样的 收件人user2
内容:邮件内容都是一样的 收件人user1
内容:邮件内容都是一样的 收件人user8
内容:邮件内容都是一样的 收件人user6
内容:邮件内容都是一样的 收件人user4
内容:邮件内容都是一样的 收件人user3
内容:邮件内容都是一样的 收件人user0
内容:邮件内容都是一样的 收件人user5
内容:邮件内容都是一样的 收件人user9

注意事项

  • 构造函数不会被执行:以二进制流的方式拷贝,重新分配 一块内存,构造函数没有被执行也是正常的

  • 深拷贝和浅拷贝

  • clone与final是两大冤家