1. 程式人生 > >一天一個設計模式(四) - 原型模式(Prototype)

一天一個設計模式(四) - 原型模式(Prototype)

管理員 ans rip prototype register 性能 除了 func comm

前言

原型模式屬於對象的創建模式。通過給出一個原型對象來指明所有創建的對象的類型,然後用這個原型對象提供的復制辦法創建出更多同類型的對象。

原型模式的結構

原型模式要求對象實現一個可以克隆自身的接口(類型)。這樣一來,通過原型實例創建新的對象,就不需要關心這個實例本身的類型,只需要實現克隆自身的方法,也而無需再去通過new來創建。

原型類型的表現形式

  1. 簡單形式
  2. 登記形式

正文

簡單形式

技術分享圖片

相關角色

  1. 客戶(Client)角色客戶類提出創建對象的請求;
  2. 抽象原型(Prototype)角色:這是一個抽象角色,通常由一個Java接口或者Java抽象類實現。此角色定義了的具體原型類所需的實現的方法。
  3. 具體原型(Concrete Prototype)角色:此角色需要實現抽象原型角色要求的克隆相關接口

示例代碼

Prototype.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 抽象原型角色
*/
public abstract class Prototype {
private String id;

public Prototype(String id) {
this.id = id;
}

public String getId() {
return id;
}

public void setId(String id) {
this.id = id;
}

/**
* 克隆自身的方法
* @return 一個從自身克隆出來的對象。
*/
public abstract Prototype clone();
}

ConcretePrototype1.java

1
2
3
4
5
6
7
8
9
10
public class ConcretePrototype1 extends Prototype {
public ConcretePrototype1(String id) {
super(id);
}

public Prototype clone() {
Prototype prototype = new ConcretePrototype1(this.getId());
return prototype;
}
}

ConcretePrototype2.java

1
2
3
4
5
6
7
8
9
10
public class ConcretePrototype2 extends Prototype {
public ConcretePrototype2(String id) {
super(id);
}

public Prototype clone() {
Prototype prototype = new ConcretePrototype2(this.getId());
return prototype;
}
}

運行結果

技術分享圖片

登記形式

技術分享圖片

相關角色

  1. 客戶(Client)角色客戶類提出創建對象的請求;
  2. 抽象原型(Prototype)角色:這是一個抽象角色,通常由一個Java接口或者Java抽象類實現。此角色定義了的具體原型類所需的實現的方法。
  3. 具體原型(Concrete Prototype)角色:此角色需要實現抽象原型角色要求的克隆相關接口
  4. 原型管理器(Prototype Manager)角色:提供各種原型對象創建管理

示例代碼

除了原型管理器Prototype Manager以外,登記模式簡單模式並無其他差異。

Prototype.java
W

1
2
3
4
5
public interface Prototype {
public Prototype clone();
public String getName();
public void setName(String name);
}

ConcretePrototype1.java

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
public class ConcretePrototype1 implements Prototype {
private String name;

@Override
public String getName() {
return this.name;
}

@Override
public void setName(String name) {
this.name = name;
}

@Override
public Prototype clone() {
Prototype prototype = new ConcretePrototype1();
prototype.setName(this.name);
return prototype;
}

@Override
public String toString() {
return "ConcretePrototype1 [name=" + name + "]";
}

}

ConcretePrototype2.java

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
public class ConcretePrototype2 implements Prototype {
private String name;

@Override
public String getName() {
return this.name;
}

@Override
public void setName(String name) {
this.name = name;
}

@Override
public Prototype clone() {
Prototype prototype = new ConcretePrototype2();
prototype.setName(this.name);
return prototype;
}

@Override
public String toString() {
return "ConcretePrototype2 [name=" + name + "]";
}
}

PrototypeManager.java

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public class PrototypeManager {
/**
* 用來記錄原型的編號同原型實例的對象關系
*/
private static Map<String, Prototype> map = new HashMap<>();

/**
* 私有化構造方法,避免從外部創建實例
*/
private PrototypeManager() {
}

/**
* 向原型管理器裏面添加或者修改原型實例
*
* @param prototypeId 原型編號
* @param prototype 原型實例
*/
public static void setProtoType(String prototypeId, Prototype prototype) {
map.put(prototypeId, prototype);
}

/**
* 根據原型編號從原型管理器裏面移除原型實例
*
* @param prototypeId 原型編號
*/
public static void removePrototype(String prototypeId) {
map.remove(prototypeId);
}

/**
* 根據原型編號獲取原型實例
*
* @param prototypeId 原型編號
* @return 原型實例對象
* @throws Exception 如果根據原型編號無法獲取對應實例,則提示異常“您希望獲取的原型還沒有註冊或已被銷毀”
*/
public static Prototype getPrototype(String prototypeId) throws Exception {
Prototype prototype = map.get(prototypeId);

if (prototype == null) {
throw new Exception("您希望獲取的原型還沒有註冊或已被銷毀");
}

return prototype;
}

}

Client.java

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
34
public class Client {
public static void main(String[] args) {
try {
// 創建第一個實例
Prototype p1 = new ConcretePrototype1();
// 註冊第一個實例
PrototypeManager.setProtoType("p1", p1);

// 克隆第一個實例的原型
Prototype p3 = PrototypeManager.getPrototype("p1").clone();
p3.setName("張三");
System.out.println("第一個實例的副本:" + p3);

// 創建第二個實例
Prototype p2 = new ConcretePrototype2();
// 註冊第二個實例
PrototypeManager.setProtoType("p2", p2);

// 克隆第二個實例的原型
Prototype p4 = PrototypeManager.getPrototype("p2").clone();
p4.setName("李四");
System.out.println("第二個實例的副本:" + p4);

// 註銷第一個實例
PrototypeManager.removePrototype("p1");
// 再次克隆第一個實例的原型
Prototype p5 = PrototypeManager.getPrototype("p1").clone();
p5.setName("王五");
System.out.println("第一個實例的副本:" + p5);
} catch (Exception e) {
e.printStackTrace();
}
}
}

運行結果

技術分享圖片

兩者之間的比較

簡單形式和登記形式的原型模式各有其長處和短處。

  1. 如果要創建的原型對象數據較少而且比較固定的話,可以采用第一種形式。在這種情況下,原型對象的引用可以由客戶端自己保存。
  2. 如果要創建的原型對象數據不固定的話,可以采用第二種形式。在這種情況下,客戶端不保存對原型對象的引用,這個任務被交給原型管理器角色。在克隆一個對象之前,客戶端可以查看管理員對象是否已經有一個滿足要求的原型對象。如果有,可以從原型管理器角色中取得這個對象引用;如果沒有,客戶端就需要自行復制此原型對象。

總結

原型模式的優點

原型模式允許在運行時動態改變具體的實現類型。原型模式可以在運行期間,有客戶來註冊符合原型接口的實現類型,也可以動態的改變具體的實現類型,看起來接口沒有任何變化,但是其實運行的已經是另外一個類實體了。因為克隆一個原型對象就類似於實例化一個類

原型模式的缺點

原型模式最主要的缺點是每一個類都必須要配備一個克隆方法。配備克隆方法需要對類的功能進行通盤考慮,這對於全新的類來說並不是很難,但是對於已有的類來說並不容易。


歡迎關註技術公眾號: 零壹技術棧

技術分享圖片零壹技術棧

本帳號將持續分享後端技術幹貨,包括虛擬機基礎,多線程編程,高性能框架,異步、緩存和消息中間件,分布式和微服務,架構學習和進階等學習資料和文章。

一天一個設計模式(四) - 原型模式(Prototype)