六、Prototype 模式

  • 一、通过复制生成的实例
  • 1.1 以下情况不根据类来生成实例
  • 1.2 如何实现不依赖类来生成实例?
  • 二、示例程序
  • 2.1 实现目标
  • 2.2 类和接口一览表
  • 2.3 代码实现
  • 三、Prototype 模式中登场的角色-
  • 四、补充
  • 4.1 相关设计模式
  • 4.2 clone 方法 和 java.lang.Cloneable 接口
  • 4.3 clone() 方法在哪里定义的?
  • 五、源代码以及原文档

Author:Gorit

Date:2021/1/13

Refer:《图解设计模式》

2021年发表博文: 15/50

一、通过复制生成的实例

在 java 中,我们可以通过 new 关键字来生成实例。我们通常这样生成的实例都是要指定类名的。

但是有时候,我们在开放的过程中,也有不指定类名来生成实例的需求。

1.1 以下情况不根据类来生成实例

在以下情况中,我们不能根据类来生成实例,而要根据已有的实例来生成新的实例。

  1. 对象种类繁多,无法将他们整合到一个类中。
  2. 难以根据类生成实例。
  3. 解耦框架与生成的实例时。

1.2 如何实现不依赖类来生成实例?

不根据类来生成实例,而是根据实例来生成实例的设计模式叫 Prototype 模式

Prototype 有原型,**模型 **的意思。在设计模式中,它是根据实例原型,实例模型来生成新的实例。

现在来回答标题提出的问题:

我们可以通过 clone 创建出实例的副本。我们还会用刀拍 clone 方法与 Cloneable 接口的使用方法。

二、示例程序

2.1 实现目标

我们要实现的程序功能是:将字符串放入方框,显示出来 或者 加上下划线显示出来

2.2 类和接口一览表

名字

说明

framework

Product

声明了抽象方法 use 和 createClone 接口

framework

Manager

调用 createClone 方法复制实例类

MessageBox

将字符串放入方框,并显示出来,实现 use 方法 和 createClone 方法

UnderlinePen

给字符串加上上下划线并显示出来的类,实现了 use 和 createClone 方法

Main

测试程序行为的类

Product 接口和 Message 类属于 framework包,负责复制实例。

Manager 类虽然会调用 createClone 方法,但是对于具体要复制哪个。并不知道。

但是只要实现了 Product 接口的类,调用它的 createClone 方法就可以复制出新的实例。

MessageBox 类 和 UnderlinePen 类是两个实现了 Product 接口的类。只用实现将这两个类“注册”到 Manager 类中,就可以随时复制新的实例。

2.3 代码实现

Product

package Prototype.framework;
// 继承复制的接口, Cloneable 通过调用 clone 实现对象的调用
public interface Product extends Cloneable{
public abstract void use(String s); // 具体是如何使用,交给子类去实现
public abstract Product createClone();
}

Manager

package Prototype.framework;
import java.util.*;

/**
* 使用 product 接口复制实例
*/
public class Manager {
// 保存名字和实例之间的对应关系
private HashMap showcase = new HashMap();
public void register(String name, Product proto) {
showcase.put(name,proto);
}

public Product create(String protoname) {
Product p = (Product) showcase.get(protoname);
return p.createClone();
}
}

MessageBox

package Prototype;

import Prototype.framework.Product;

public class MessageBox implements Product {
private char decochar;

public MessageBox(char decochar) {
this.decochar = decochar;
}

public void use(String s) {
int length = s.getBytes().length;
for (int i=0;i<length+4;i++) {
System.out.print(decochar);
}
System.out.println("");
System.out.println(decochar + " " + s + " " + decochar);
for (int i=0;i<length+4;i++) {
System.out.print(decochar);
}
System.out.println("");
}

public Product createClone() {
Product p = null;
try {
p = (Product) clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}

UnderlinePen

package Prototype;

import Prototype.framework.Product;

public class UnderlinePen implements Product {
private char ulchar;

public UnderlinePen(char ulchar) {
this.ulchar = ulchar;
}

public void use(String s) {
int length = s.getBytes().length;
System.out.println("\"" + s + "\"");
System.out.println(" ");
for (int i=0;i< length;i++) {
System.out.print(ulchar);
}
System.out.println("");
}

public Product createClone() {
Product p = null;
try {
p = (Product) clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}

Main

package Prototype;

import Prototype.framework.Manager;
import Prototype.framework.Product;

public class Main {

public static void main(String[] args) {
// 准备
Manager manager = new Manager();
UnderlinePen upen = new UnderlinePen('~');
MessageBox mbox = new MessageBox('*');
MessageBox sbox = new MessageBox('/');
manager.register("strong msg",upen);
manager.register("warning box",mbox);
manager.register("slash box",sbox);

// 生成
Product p1 = manager.create("strong msg");
p1.use("Hello World");
Product p2 = manager.create("warning box");
p2.use("Hello World");
Product p3 = manager.create("slash box");
p3.use("Hello World");

}
}

执行效果

23种设计模式(六) —— 手写实现 Prototype 模式 (生成实例)_java

三、Prototype 模式中登场的角色-

  • Prototype(原型)

Product 角色负责定义用于复制现有实例来生成新实例的方法。 Product 接口扮演此角色

  • ConcretePrototupe(具体的原型)

ConcretePrototype 角色负责实现复制现有的实例并生成新实例的方法。在上述程序中, MessageBox类 和 UnderlinePen 类扮演此角色

  • Client (使用者)

Client 角色负责使用复制实例的方法生成新的实例。在示例中,由 Message类 扮演此角色。

四、补充

4.1 相关设计模式

  • Flyweight 模式(可以在不同地方使用同一个实例)
  • Memento 模式(可以保存当前实例状态,以实现快照和撤销功能)
  • Composite 模式 以及 Decorator 模式(动态创建复杂结构的实例,这里可以用 Prototype 模式快速生成实例)
  • Command 模式(该模式中出现的命令需要复制的时候,可以使用 Prototype)

4.2 clone 方法 和 java.lang.Cloneable 接口

要使用 clone 方法进行复制实例,就要实现 Cloneable 接口,否则就会出现 CloneNotSupportedException

java.lang 包是默认引入的。因此不必显示引入 java.lang 包调用 clone() 方法

4.3 clone() 方法在哪里定义的?

clone() 方法定义在 java.lang.Object 中,因为 Object 类是所有 java 类的父类。因此所有类都默认继承了 Object 类。