
Prototype模式——通过复制生成实例(原型模式)
Prototype模式的使用场景
当我们生成一个 Something 类的实例时,通常会使用以下形式:
new Something()
在 Java 中,我们可以使用 new 关键字指定类名来生成类的实例。像这样使用 new 来生成时,是必须指定类名的。
当我们在开发中,需要“不指定类名的前提下生成实例”时,比如:
对象种类繁多,无法将他们整合到一个类中时
第一种情况就是当需要处理的对象过多,如果将他们分别作为一个类,那么必须编写多个类。
难以根据类生成实例时
当生成实例的过程太过复杂,很难根据类来生成实例。例如:“我们生成一个 用户在图形编辑器中使用鼠标制作出的图形的实例 ”,我们如果想要创建出和操作一样的实例时,会比较困难。
通常,我们会实现操作出的实例保存,然后在需要的时候通过复制来生成新的实例。
想解耦框架与生成的实例时
如果想要让生成实例的框架不依赖于具体的类。此时,不能指定类名来生成实例,而是事先“注册”一个“原型“实例,然后通过复制该实例来生成新的实例。
示例程序
类与接口一览表:
示例程序的类图:
Product接口
Product
接口是复制功能的接口。该接口继承了 java.lang.Cloneable
接口。
use
:用于“使用”的方法。具体交给子类实现。createClone
:用于复制实例的方法。
public interface Product extends Cloneable {
void use(String s);
Product createClone();
}
Manager类
使用 Product接口 来复制实例。
showcase
字段:java.util.HashMap
类型,它保存了实例的“名字”和“实例“之间的对应关系。register
方法:将会接受到1组”名字”和“Product 接口” 注册到showcase中。
也就是说它实现了 Product
接口的类的实例,而不是实现了Product。这意味着,它可以单独的修改Product和Manager,不受MessageBox和UnderlinePen类的影响。
public class Manager {
private HashMap<String, Product> showcase = new HashMap<>();
public void register(String name, Product proto) {
showcase.put(name, proto);
}
public Product create(String protoname) {
Product p = showcase.get(protoname);
return p.createClone();
}
}
MessageBox类
MessageBox类实现了Product接口。
decochar
字段:特殊的字符(即中心字符串四周的边框)use
方法:使用decochar字段中的字符把要显示的字符串框起来
比如,让显示字符串“hello”时,将会显示:
*********
* Hello *
*********
createClone
方法:用于复制自己。它内部所调用的 clone 方法是Java定义的方法,用于复制自己。
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类
与MessageBox类似
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.print(" ");
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
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 message", upen);
manager.register("warning box", mbox);
manager.register("slash box", sbox);
// 生成
Product p1 = manager.create("strong message");
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(原型)
由 Product 扮演,用于定义复制现有实例来生成新实例的方法。
ConcretePrototype(实现的原型)
由 MessageBox类 和 UnderlinePen类 扮演,负责实现复制现有实例生成新实例的方法。
Client(使用者)
由 Manager类 扮演,负责使用复制实例的方法生成新的实例。
Prototype模式类图
拓展思路
不能根据类来生成实例吗
回到最初的使用场景:
- 对象种类繁多,无法整合到一个类中
根据示例程序,我们每个类都有不同的样式,如 '~', ‘*’
如果这些样式都要编写为一个类,那么类的数量将会非常庞大。
- 难以根据类生成实例时
还是和上面说的一样,我们如果想要创建出和操作一样的实例时,会比较困难
- 想解耦框架与生成的实例时
在Manager的create方法中,我们没有使用类名来为生成的实例明明,这种方式具有更好的通用性,并且将框架从类名的束缚中解脱。
- 感谢你赐予我前进的力量