目錄
一、簡介
1、定義
原型模式就是從一個對象再創建另一個可定製的對象,而不需要知道任何創建的細節。
2、使用場景
我們現在一般會使用new關鍵字指定類名生成類的實例(PS:我們以前使用java.lang.Cloneable的一個很大原因是使用new創建對象的速度相對來說會慢一些,隨着JVM性能的提升,new的速度和Object的clone()方法的速度差不多了。)。
使用new關鍵字創建類的時候必須指定類名,但是在開發過程中也會有“在不指定類名的前提下生成實例”的需求。例如,在下面這些情況下,就需要根據現有的實例來生成新的實例。
1) 對象種類繁多,無法將他們整合到一個類的時候;
2) 難以根據類生成實例時;
3) 想解耦框架與生成的實例時。
如果想要讓生成實例的框架不再依賴於具體的類,這時,不能指定類名來生成實例,而要事先“註冊”一個“原型”實例,然後通過複製該實例來生成新的實例。
3、模式分析
在原型模式結構中定義了一個抽象原型類,所有的Java類都繼承自java.lang.Object,而Object類提供一個clone()方法,可以將一個Java對象複製一份。因此在Java中可以直接使用Object提供的clone()方法來實現對象的克隆,Java語言中的原型模式實現很簡單。
能夠實現克隆的Java類必須實現一個標識接口Cloneable,表示這個Java類支持複製。如果一個類沒有實現這個接口但是調用了clone()方法,Java編譯器將拋出一個CloneNotSupportedException異常。
注意: `java.lang.Cloneable 只是起到告訴程序可以調用clone方法的作用,它本身並沒有定義任何方法。
在使用原型模式克隆對象時,根據其成員對象是否也克隆,原型模式可以分爲兩種形式:深克隆 和 淺克隆 。
4、優缺點
原型模式的優點:
- 當創建新的對象實例較爲複雜時,使用原型模式可以簡化對象的創建過程,通過一個已有實例可以提高新實例的創建效率。
- 可以動態增加或減少產品類。
- 原型模式提供了簡化的創建結構。
- 可以使用深克隆的方式保存對象的狀態。
原型模式的缺點:
- 需要爲每一個類配備一個克隆方法,而且這個克隆方法需要對類的功能進行通盤考慮,這對全新的類來說不是很難,但對已有的類進行改造時,不一定是件容易的事,必須修改其源代碼,違背了“開閉原則”。
- 在實現深克隆時需要編寫較爲複雜的代碼。
二、實現
1、UML類圖
2、Prototype
package com.mfc.design.原型模式;
/**
* @author MouFangCai
* @date 2019/10/12 14:41
*/
public abstract class Prototype implements Cloneable {
private String id;
protected String type;
abstract void draw();
public String getType(){
return type;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
@Override
public Object clone() {
Object clone = null;
try {
clone = super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return clone;
}
}
3、ConcretePrototypeA
package com.mfc.design.原型模式;
/**
* @author MouFangCai
* @date 2019/10/12 14:45
*/
public class ConcretePrototypeA extends Prototype {
@Override
void draw() {
System.out.println("這是ConcretePrototypeA的draw()");
}
public ConcretePrototypeA(){
type = "ConcretePrototypeA";
}
}
4、ConcretePrototypeB
package com.mfc.design.原型模式;
/**
* @author MouFangCai
* @date 2019/10/12 14:45
*/
public class ConcretePrototypeB extends Prototype {
@Override
void draw() {
System.out.println("這是ConcretePrototypeB的draw()");
}
public ConcretePrototypeB(){
type = "ConcretePrototypeB";
}
}
5、PrototypeCache
package com.mfc.design.原型模式;
import java.util.Hashtable;
/**
* @author MouFangCai
* @date 2019/10/12 14:45
*/
public class PrototypeCache {
// 維護一個註冊表
private static Hashtable<String, Prototype> prototypeMap = new Hashtable<>();
// 提供一個獲取新實例的方法
public static Prototype getprototype(String prototypeId) {
Prototype cached = prototypeMap.get(prototypeId);
return (Prototype) cached.clone();
}
// 對每種形狀都運行數據庫查詢,並創建該形狀
// prototypeMap.put(prototypeId, Prototype);
// 例如,我們要添加2種Prototype
public static void loadCache() {
ConcretePrototypeA A = new ConcretePrototypeA();
A.setId("1");
prototypeMap.put(A.getId(),A);
ConcretePrototypeB B = new ConcretePrototypeB();
B.setId("2");
prototypeMap.put(B.getId(),B);
}
}
6、Client_Prototype
package com.mfc.design.原型模式;
/**
* @author MouFangCai
* @date 2019/10/12 15:00
*/
public class Client_Prototype {
public static void main(String[] args) {
PrototypeCache.loadCache();
Prototype clonedPrototype1 = (Prototype) PrototypeCache.getprototype("1");
System.out.println("Prototype : " + clonedPrototype1.getType());
Prototype clonedPrototype2 = (Prototype) PrototypeCache.getprototype("2");
System.out.println("Prototype : " + clonedPrototype2.getType());
}
}
三、原型模式分析
上面這個例子,參考自菜鳥教程:https://www.runoob.com/design-pattern/prototype-pattern.html(小編不是很理解這個原型模式的例子,感覺似乎沒什麼實際意義,只是new對象的操作換了個地方而已)
從上面這個例子分析,總感覺原型模式似乎有些問題。所以小編再次參考閱讀了《大話設計模式》,詳情請見下面的例子
1、問題描述
比如說,有一個使用場景需要不同的Person對象,但是其name屬性一樣,但是age和score屬性需要根據環境不同而設置不同的值
2、Person_Prototype
package com.mfc.design.原型模式2;
/**
* @author MouFangCai
* @date 2019/10/12 16:12
*/
// 原型Prototype
public class Person_Prototype implements Cloneable {
private String name;
private int age;
private int score;
// 設置需要複製的屬性(公有屬性)
public Person_Prototype(String name){
this.name = name;
}
// 設置需要自定義的屬性
public void setMessage(int age, int score){
this.age =age;
this.score =score;
}
public void dispaly(){
System.out.println("name:" + name + "----age:"+ age + "----score:" + score);
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
3、Cilent客戶端
package com.mfc.design.原型模式2;
/**
* @author MouFangCai
* @date 2019/10/12 15:00
*/
public class Client_Prototype {
public static void main(String[] args) throws CloneNotSupportedException {
Person_Prototype p1 = new Person_Prototype("mfc");
p1.setMessage(18, 22);
// 複製一個新的Person,並修改對應的細節屬性
Person_Prototype p2 = (Person_Prototype)p1.clone();
p2.setMessage(24,44 );
System.out.println("是否是同一個對象:" + (p1 == p2));
p1.dispaly();
p2.dispaly();
}
}
結果如下:
是否是同一個對象:false
name:mfc----age:18----score:22
name:mfc----age:24----score:44Process finished with exit code 0
4、分析
從運行結果,我們可以看到,Person對象被複制了一個,同時name屬性保持一致,age被重新自定義,達到我們預期要求
備註:這只是 淺複製,如果Person對象有一個私有屬性是對象時,就需要使用 深複製 進行實現,有興趣者可自行百度。