原型模式 prototype 建立型 設計模式(七)

原型模式  prototype

意圖

用原型實例指定須要建立的對象的類型,而後使用複製這個原型對象的方法建立出更多同類型的對象
 
顯然,原型模式就是給出一個對象,而後克隆一個或者更多個對象
小時候看過的動畫片《西遊記》,主題曲猴哥中有一句「拔一根毫毛 ,吹出猴萬個 」這就是原型模式
孫悟空做爲原型對象,「拔一根毫毛 ,吹」 這就是調用複製對象方法,「猴」萬個,就是結果了,建立了「萬個」 猴子 

原型模式的根本-拷貝

原型模式的根本在於對象的拷貝
說白了就是: 如何複製一個對象?
 
對象的表示
Object ref = new Object();
上面這句話能夠理解爲三個步驟
  1. 建立一個Object類型的引用名爲 ref
  2. 建立了一個Object類型的對象
  3. 使變量ref指向這個新的對象的內存地址
image_5bf3c33c_7a92
 
一個對象內部有一個或多個屬性
屬性多是基本類型也多是一個引用類型
而對於引用類型就至關於咱們上面表述的這種形式
Object ref = new Object();
 
引用變量指向實際的對象
 
深拷貝和淺拷貝
拷貝有兩種形式, 淺拷貝和深拷貝
淺拷貝被複制的對象全部的屬性成員變量都含有與原來的對象相同的值
也就是說 若是是引用類型,他仍舊會指向原來的對象,也就是全部的備份中的引用類型指向的是同一個對象
淺拷貝僅僅拷貝當前對象自己
image_5bf3c33c_58c1
 
深拷貝則偏偏相反, 深拷貝將會拷貝全部的對象
也就是 若是內部有成員變量爲引用類型,那麼也會拷貝被指向的對象,不只僅是拷貝當前對象自己
全部被引用到的對象都複製了一遍
image_5bf3c33c_7e15
 
對於深拷貝,能夠藉助於序列化實現,深拷貝一個對象

結構

原型模式做爲建立型模式的一種
與工廠模式建造者模式是相似的,都是爲了建立對象
只不過是建立的方式不一樣
原型模式的建立邏輯則是藉助於已經存在的對象,調用他的拷貝方法,從而建立相同類型的新的對象
根據依賴倒置原則,面向抽象而不是細節進行編程,因此使用抽象角色Prototype用於描述原型類
一種通用的結構描述形式爲:
image_5bf3c33c_6de5
 
Client 客戶端角色
客戶端程序發起建立對象請求         
Prototype 抽象原型角色
抽象角色用於描述原型類,給出具體原型類須要的協議 接口或者抽象類
ConcretePrototype具體原型角色
被複制的對象類型

代碼示例

package prototype;
public interface Prototype extends Cloneable {
Prototype clone();
}
package prototype;
public class ConcreatePrototype implements Prototype {
    @Override
    public Prototype clone() {
        try{
            return (Prototype)super.clone();
        }catch (CloneNotSupportedException e){
            return null;
        }
    }
}
package prototype;
public class Client {
    public static void main(String[] args){
        Prototype prototype = new ConcreatePrototype();
        Prototype clonedObj = (Prototype)prototype.clone();
        System.out.println(clonedObj.getClass());
        System.out.println(prototype == clonedObj);
    }
}
image_5bf3c33c_60bf

Java自然的原型模式

在Java中, 全部的對象都繼承自Java.lang.Object
Object中有clone()方法 ,能夠將一個對象進行拷貝
因此說Java天生的內置了原型模式---經過對象的clone方法進行對象的拷貝
不過也有一些具體的規則須要注意
 
Java語言提供了 Cloneable接口,做爲 標記接口
凡是實現了Cloneable接口的類都聲稱:「能夠安全的在這個類上使用clone()方法」。
試圖調用clone()方法時, 若是此對象的類沒有實現 Cloneable 接口,則會拋出 CloneNotSupportedException
 
clone()方法以下
image_5bf3c33c_5cb7
clone方法是淺拷貝而不是深拷貝
Object中的clone()方法規定了拷貝的通常協議,能夠參看API文檔  
  1. 對於任何對象 x,表達式:x.clone() != x,克隆對象與原始對象不是同一個對象
  2. x.clone().getClass() == x.getClass() ,克隆對象與原始對象是同一種類型
  3. x.clone().equals(x) 爲true
這三點並非必須的,並非必須的,並非必須的,可是除非特殊狀況,不然建議應該都知足 
 
Object 類自己不實現接口 Cloneable
因此,若是在類型爲Object的對象上調用clone方法,會拋出異常
 
Java語言經過Object提供的protected方法以及Cloneable標記接口機制
定義了一套複製對象的工做流程:
  1. 實現Cloneable接口
  2. 覆蓋或者使用繼承而來的clone()方法
對於最簡單的原型模式 的應用,只須要原型類完成這步便可
這就是Java中原型模式型使用方式
 
由於在Java中,全部的對象直接或者間接地繼承Object
因此始終內置的隱含了Object這一抽象角色  
咱們的示例代碼中,Prototype 就是至關於java中的Object
 
圖中Prototype 爲具體的原型類(提供了clone方法的類)
image_5bf3c33c_74d8

擁有管理器的原型模式

原型模式中最爲關鍵的是調用某個對象的拷貝方法,進行原始對象的複製
因此原型模式一種常見的用法就是藉助於這個"原始對象",達到工廠方法的效果
客戶端中保存一個ConcretePrototype類型的對象
後續的對象建立獲取就是客戶端經過這個內部的對象,調用它的拷貝方法進行進一步的操做
 
若是產品結構比較簡單,可能只須要幾種類型的對象便可
上面的原型結構比較適合,客戶端本身保存全部的對象
可是
若是產品等級結構比較雜亂,或者說要建立的原型對象是數目並非固定的
又能夠進一步將管理對象的職責進行提取分離,抽象出來一個管理器的角色
專門用於管理這些對象
 
這種帶管理器的原型模式中,客戶端就不在持有原型對象的引用了,也就是客戶端不在擁有原型對象
取而代之的是經過管理器獲取
image_5bf3c33c_489d
Client 客戶端角色
向管理員發起建立對象的請求
Prototype、ConcretePrototype 角色與以前的概念相同
PrototypeManager 角色
原型管理器角色,負責原型對象的建立和管理

示例代碼

在原來的基礎上增長原型管理器
package prototype;
import java.util.HashMap;
public class PrototypeManager {
    /*hashMap維護原型對象
    * */
    private HashMap<String,Object> map = new HashMap<>();
    /*餓漢式單例模式返回建立原型對象管理器
    邏輯上原型對象管理器只有一個
    * */
    private static PrototypeManager prototypeManager= new PrototypeManager();
    public static PrototypeManager getPm(){
        return prototypeManager;
    }
    /*初始化內置兩個原型對象
    * */
    private PrototypeManager(){
        map.put("product1",new ConcreatePrototype());
        map.put("product2",new ConcreatePrototype());
    }
    /*提供了添加原型對象方法*/
    public void add(String key,Prototype prototype){
        map.put(key,prototype);
    }
    /*提供了獲取對象的方法,獲取的對象依賴的是clone,而不是保存進去的對象*/
    public Prototype get(String key){
        return ((Prototype)map.get(key)).clone();
    }
}
測試類Client角色中也增長相關代碼
image_5bf3c33c_7ec3
看得出來,從對象管理器中獲取的對象,都是原有對象的一個clone  並非相同的對象
帶管理器的原型模式也叫作 登記形式的原型模式
登記就是至關於在管理器中備案

適用場景

爲何要使用原型模式?
簡簡單單的new一個對象很差麼?爲何非要複製呢?
 
當建立新的對象的過程較爲複雜時,使用原型模式能夠簡化對象的建立過程
好比初始化佔用時間較長,這種狀況下建立一個對象將再也不簡單,因此考慮原型模式
 
對於工廠模式中,工廠須要有與產品等級結構相同的結構
一個工廠生產一種產品
然而,若是產品的等級結構容易發生變化的話,工廠類的等級結構可能不得不進行變化
也就是說對於產品等級結構容易變化的場景來講,工廠模式將會不方便
若是採用 原型模式,那麼就再也不須要關注產品的等級結構,產品的等級結構能夠隨意變更
由於原型模式僅僅關注具體的產品對象,對象之間的結構以及結構的變化並不會產生影響
因此在 這種狀況下,原型模式擁有比工廠模式更爲靈活,擴展性更好
不過
代價是每一個類中都必須有一個clone方法,對象的建立邏輯從每一個工廠轉移到了每一個clone方法中
 
在框架中使用原型模式能夠與生成的實例進行解耦
框架中面向抽象進行編程,只關注他的抽象類型,不關注他的具體類型
具體的對象能夠經過配置文件等方式注入
框架藉助於原型模式能夠得到這種類型的對象,而徹底不用關注這個類型
不然當你使用某種類型時,你必然須要建立對象,也就是始終要接觸到具體的類型
這種方法就可讓你永遠不知道具體的類型,完全的分離

總結

原型模式的根本在於複製,因此依賴拷貝方法,java也內置了這種模式
在java中使用時,只須要實現Cloneable接口而且重寫或者使用繼承而來的clone方法便可
原型模式能夠很好地隱藏建立對象的具體類型
當建立一個對象變得複雜時,咱們能夠考慮使用原型模式 經過複製的方式簡化對象的建立過程
可是這有一個前提,那就是複製對象相對比較簡單
可是,可是,可是,有的時候,複製一個對象自己卻也是很是複雜的,通常能夠藉助於序列化來進行
並且,每個類都須要擁有clone方法,當須要對已有的類進行擴展改造時,clone方法也須要進行修改
這並不複合開閉原則
相關文章
相關標籤/搜索