前言
原型模式屬於物件的建立模式。通過給出一個原型物件來指明所有建立的物件的型別,然後用這個原型物件提供的複製辦法建立出更多同型別的物件。
原型模式的結構
原型模式要求物件實現一個可以克隆自身的介面(型別)。這樣一來,通過原型例項建立新的物件,就不需要關心這個例項本身的型別,只需要實現克隆自身的方法,也而無需再去通過new
來建立。
原型型別的表現形式
- 簡單形式
- 登記形式
正文
簡單形式
相關角色
- 客戶(Client)角色:客戶類提出建立物件的請求;
- 抽象原型(Prototype)角色:這是一個抽象角色,通常由一個
Java
介面或者Java
抽象類實現。此角色定義了的具體原型類所需的實現的方法。 - 具體原型(Concrete Prototype)角色:此角色需要實現抽象原型角色要求的克隆相關的介面。
示例程式碼
Prototype.java
/**
* 抽象原型角色
*/
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
public class ConcretePrototype1 extends Prototype {
public ConcretePrototype1(String id) {
super(id);
}
public Prototype clone() {
Prototype prototype = new ConcretePrototype1(this.getId());
return prototype;
}
}
複製程式碼
ConcretePrototype2.java
public class ConcretePrototype2 extends Prototype {
public ConcretePrototype2(String id) {
super(id);
}
public Prototype clone() {
Prototype prototype = new ConcretePrototype2(this.getId());
return prototype;
}
}
複製程式碼
執行結果
登記形式
相關角色
- 客戶(Client)角色:客戶類提出建立物件的請求;
- 抽象原型(Prototype)角色:這是一個抽象角色,通常由一個
Java
介面或者Java
抽象類實現。此角色定義了的具體原型類所需的實現的方法。 - 具體原型(Concrete Prototype)角色:此角色需要實現抽象原型角色要求的克隆相關的介面。
- 原型管理器(Prototype Manager)角色:提供各種原型物件的建立和管理。
示例程式碼
除了原型管理器Prototype Manager
以外,登記模式和簡單模式並無其他差異。
Prototype.java
W
public interface Prototype {
public Prototype clone();
public String getName();
public void setName(String name);
}
複製程式碼
ConcretePrototype1.java
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
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
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
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();
}
}
}
複製程式碼
執行結果
兩者之間的比較
簡單形式和登記形式的原型模式各有其長處和短處。
- 如果要建立的原型物件資料較少而且比較固定的話,可以採用第一種形式。在這種情況下,原型物件的引用可以由客戶端自己儲存。
- 如果要建立的原型物件資料不固定的話,可以採用第二種形式。在這種情況下,客戶端不儲存對原型物件的引用,這個任務被交給原型管理器角色。在克隆一個物件之前,客戶端可以檢視管理員物件是否已經有一個滿足要求的原型物件。如果有,可以從原型管理器角色中取得這個物件引用;如果沒有,客戶端就需要自行復制此原型物件。
總結
原型模式的優點
原型模式允許在執行時動態改變具體的實現型別。原型模式可以在執行期間,有客戶來註冊符合原型介面的實現型別,也可以動態的改變具體的實現型別,看起來介面沒有任何變化,但是其實執行的已經是另外一個類實體了。因為克隆一個原型物件就類似於例項化一個類。
原型模式的缺點
原型模式最主要的缺點是每一個類都必須要配備一個克隆方法。配備克隆方法需要對類的功能進行通盤考慮,這對於全新的類來說並不是很難,但是對於已有的類來說並不容易。
歡迎關注技術公眾號: 零壹技術棧
本帳號將持續分享後端技術乾貨,包括虛擬機器基礎,多執行緒程式設計,高效能框架,非同步、快取和訊息中介軟體,分散式和微服務,架構學習和進階等學習資料和文章。