设计模式-原型模式

原型模式

定义

在Java中,我们可以使用new关键字指定类名来生成类的实例。像这样使用new来生成实例时,是必须指定类名的。但是,在开发过程中,有时候也会有“在不指定类名的前提下生成实例”的需求,例如,在以下情况下,我们就不能根据类来生成实例,而要根据现有的实例来生成新的实例。

对象种类繁多,无法将它们整合到一个类中时

需要处理的对象太多,如果将它们分别作为一个类,必须要编写很多个类文件

难以根据类生成实例时

生成实例的过程太复杂,很难根据类来生成实例。通常,在想生成一个和之前用户通过操作所创建出的实例完全一样的实例的时候,我们会事先将用户通过操作所创建出的实例保存起来,然后在需要时通过复制来创建新的实例。

想解耦框架与生成的实例时

想要让生成实例的框架不依赖于具体的类。这时,不能指定类名来生成实例,而要事先“注册”一个“原型”实例,然后通过复制该实例来生成新的实例。

类图

原型模式类图

Prototype(原型)

Prototype负责定义用于复制现有实例来生成新实例的方法。

ConcretePrototype(具体的原型)

负责实现复制现有实例并生成新实例的方法

Client(使用者)

负责使用复制实例的方法生成新的实例。

示例

类图

原型模式示例类图

Product

继承了Cloneable接口

1
2
3
4
public interface Product extends Cloneable {
public abstract void use(String s);
public abstract Product createClone();
}

Manager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class Manager {
private HashMap<String, Product> showcase = new HashMap<>();

public void register(String name, Product product) {
showcase.put(name, product);
}

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

}

MessageBox

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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) {

}
return p;
}
}

UnderlinePen

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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("");
}

@Override
public Product createClone() {
Product p = null;
try {
p = (Product) clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return p;
}

}

Main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Main {

public static void main(String[] args) {
Manager manager = new Manager();

UnderlinePen pen = new UnderlinePen('~');

MessageBox mBox = new MessageBox('*');
MessageBox sBox = new MessageBox('/');
manager.register("strong message", pen);
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");
}

}

总结

  1. clone方法定义在java.lang.Object中。因为Object类是所有Java类的父亲,因为所有的Java类都继承了clone方法。
  2. cloneable接口中并没有声明任何方法。它只是被用来标记“可以使用clone方法进行复制的”。这样的接口被称为标记接口
  3. clone方法所进行的复制只是将被复制实例的字段值直接复制到新的实例中。换言之,它并没有考虑字段中所保存的实例的内容。例如,当字段中保存的是数组时,如果使用clone方法进行复制,则只会复制该数组对象的引用,并不会一一复制数组中的元素。向上面这样字段对字段的复制被称为浅复制。clone方法进行的复制,就是浅复制
  4. 当使用clone方法进行浅复制无法满足需求时,类的设计者可以实现重写clone方法,实现自己需要的复制功能(重写clone方法时,不要忘记使用super.clone()来调用父类的clone方法)。
  5. clone方法只会进行复制,并不会调用被复制实例的构造函数。此外,对于在生成实例时需要进行特殊的初始化处理的类,需要自己去实现clone方法,在其内部进行这些初始化处理。
0%