原型模式的运用
Prototype模式——通过复制生成实例。
在Java中,我们可以使用new关键字指定类名来生成类的实例。像这样使用new来生成实例时,是必须指定类名的。但是,在开发过程中,有时候也会有“在不指定类名的前提下生成实例”的需求。例如,在以下的情况下,我们就不能根据类来生成实例,而需要根据现有的实例来生成新的实例。
1.对象种类繁多,无法将它们整合到一个类时
第一种情况是需要处理的对象太多,如果将它们分别作为一个类,必须要编写很多个类文件。
2.难以根据类生成实例时
第二种情况是生成实例的过程太过复杂,很难根据类来生成实例。例如,我们假设这里有一个实例,即表示用户在图形编辑器中使用鼠标制作出的图形实例。想在程序中创建这样的实例是非常困难的。通常,在想生成一个和之前用户通过操作所创建出的实例完全一样的实例的时候,我们事先将用户所创建出的实例保存起来,然后在需要时通过复制来生成新的实例。
3.想解耦框架与生成的实例时
第三种情况是想要让生成实例的框架不依赖于具体的类。这时,不能指定类名来生成实例,而要事先“注册”一个“原型”实例,然后通过复制该实例来生成新的实例。
根据实例生成实例与使用复印文档相类似。即使不知道原来的文档中的内容,我们也可以使用复印机复制出完全相同的文件,无论多少份都行。
示例程序
示例程序类图
Product接口
public interface Product extends Cloneable{
void use(String s);
Product createClone();
}
Manager类
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();
}
}
UnderlinePen类
public class UnderlinePen implements Product {
private char ulchar;
public UnderlinePen(char ulchar) {
this.ulchar = ulchar;
}
@Override
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(ulchar);
}
@Override
public Product createClone() {
Product p = null;
try {
p = (Product) clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return p;
}
}
MessageBox类
public class MessageBox implements Product {
private char decochar;
public MessageBox(char decochar) {
this.decochar = decochar;
}
@Override
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("");
}
@Override
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模式中登场角色
1.Prototype(原型)
Product角色负责定义用于复制现有实例来生成新实例的方法。在示例程序中,由Product接口扮演此角色。
2.ConcretePrototype(具体的原型)
ConcretePrototype角色负责实现复制现有实例并生成新实例的方法。在示例程序中,由MessageBox类和UnderlinePen类扮演此角色。
3.Client(使用者)
Client角色负责使用复制实例的方法生成新的实例。在示例程序中,由Manager类扮演此角色。
Prototype模式的通用类图
JavaClone接口
实现了Cloneable接口的类的实例可以调用clone方法进行复制,clone方法的返回值是复制出得的新的实例(clone方法内部所进行的处理是分配与要复制的实例同样大小的内存空间,接着将要复制的实例中的字段的值复制到所分配的内存空间中去)。
clone方法所进行的复制只是将被复制实例的字段值直接复制到新的实例中。换言之,它并没有考虑字段中所保存的实例的内容。例如,当字段中保存的是数组时,如果使用clone方法进行复制,则只会复制该数组的引用,并不会一一复制数组中的元素。
像上面这样的字段对字段的复制被称为浅复制。clone方法所进行的复制就是浅复制。
clone方法只会进行复制,并不会调用被复制实例的构造函数。