六、Prototype 模式
Author:Gorit
Date:2021/1/13
Refer:《图解设计模式》
2021年发表博文: 15/50
一、通过复制生成的实例
在 java 中,我们可以通过 new 关键字来生成实例。我们通常这样生成的实例都是要指定类名的。
但是有时候,我们在开放的过程中,也有不指定类名来生成实例的需求。
1.1 以下情况不根据类来生成实例
在以下情况中,我们不能根据类来生成实例,而要根据已有的实例来生成新的实例。
- 对象种类繁多,无法将他们整合到一个类中。
- 难以根据类生成实例。
- 解耦框架与生成的实例时。
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");
}
}
执行效果
三、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 类。