原型模式是什么?

原型模式(Prototype Pattern)是一种创建型的设计模式。根据 GoF 对原型模式的定义:

Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.

它的意思是使用原型的实例指定要创建对象的种类,并通过拷贝此原型创建新的对象。原型模式就是对象的拷贝,关于 Java 语言的对象拷贝,可参考 https://www.yuque.com/huey/java/shallow-copy-and-deep-copy
原型模式为所有支持克隆的对象声明了一个通用的接口,该接口让我们能够克隆对象,但把复制过程委派给被克隆的实际对象。

UML 类图

用 UML 类图来描述原型模式的结构,在模式中各个角色之间的关系:
image.png
根据上图,总结了模式中各个角色的职责以及它们之间的关系:
原型是声明了克隆方法的接口,并且在大多数情况下,它也只声明这个方法。
具体原型会实现克隆方法。
客户端可以复制实现了原型接口的任何对象。

案例

让我们通过一个案例来帮助我们进一步理解原型模式。首先是原型接口的声明,它很简单,只有一个 clone 方法。

  1. public interface Prototype<T> {
  2. public T clone();
  3. }

模型以图书为例,一本书有书名、作者等属性,其中作者以 Person 类表示,它包含了姓和名两个属性。它们都基于私有的拷贝构造方法实现了克隆方法。

  1. public class Book implements Prototype<Book> {
  2. private String name;
  3. private Person author;
  4. // getters and setters
  5. public Book(String name, Person author) {
  6. this.name = name;
  7. this.author = author;
  8. }
  9. private Book(Book book) {
  10. this(book.getName(), book.getEdition(), book.getAuthor() != null ? book.getAuthor().clone() : null);
  11. }
  12. @Override
  13. public Book clone() {
  14. return new Book(this);
  15. }
  16. }
  17. public class Person implements Prototype<Person> {
  18. private String firstName;
  19. private String lastName;
  20. // getters and setters
  21. public Person(String firstName, String lastName) {
  22. this.firstName = firstName;
  23. this.lastName = lastName;
  24. }
  25. private Person(Person person) {
  26. this(person.getFirstName(), person.getLastName());
  27. }
  28. @Override
  29. public Person clone() {
  30. return new Person(this);
  31. }
  32. @Override
  33. public String toString() {
  34. return firstName + " " + lastName;
  35. }
  36. }

即使还有继承关系,仍然可以通过拷贝构造方法来实现克隆方法。

  1. public class EBook extends Book {
  2. private String format;
  3. // getter and setter
  4. public EBook(String name, Person author, String format) {
  5. super(name, author);
  6. this.format = format;
  7. }
  8. private EBook(EBook eBook) {
  9. this(eBook.getName(), eBook.getAuthor() != null ? eBook.getAuthor().clone() : null, eBook.getFormat());
  10. }
  11. @Override
  12. public EBook clone() {
  13. return new EBook(this);
  14. }
  15. }

案例源码

可在 GitHub 上查看上述案例的完整代码。

参考资料

以下是本文参考的资料: