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

来源:https://www.cnblogs.com/noteless/archive/2018/11/20/9989647.html
-Advertisement-
Play Games

原型模式 prototype也是常用的一種設計模式,java中已經內置,藉助於Object的clone方法,本文對原型模式進行了簡單介紹,以及Java實現,並且介紹了對象的深拷貝與淺拷貝。 ...


原型模式  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方法也需要進行修改 這並不複合開閉原則  
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 如今,社會的飛速發展和新興企業的不斷添加,各行各業之間的競爭可以用慘烈來形容了。越來越多的企業逐步開拓互聯網市場甚至是由實體轉向互聯網市場,對於那些剛觸網的企業來說,一切都是從零開始。萬事開頭難,進軍互聯網,先要有一個高瞻遠矚的互聯網策略,緊接著就是頭疼的網站伺服器這塊了。一般對於初次嘗試互聯網的企 ...
  • 方式1:路由路徑攜帶參數(param/query) 1) 配置路由 2) 路由路徑 3) 路由組件中讀取請求參數 方式2:<router-view>屬性攜帶數據 ,這個是app.vue 使用方法: ...
  • 歡迎大家前往 "騰訊雲+社區" ,獲取更多騰訊海量技術實踐乾貨哦~ 本文由 "J2X" 發表於 "雲+社區專欄" 我們都有過上機器查日誌的經歷,當集群數量增多的時候,這種原始的操作帶來的低效率不僅給我們定位現網問題帶來極大的挑戰,同時,我們也無法對我們服務框架的各項指標進行有效的量化診斷,更無從談有 ...
  • 工廠模式分為三種:簡單工廠模式,工廠方法模式,抽象工廠模式。我看有的書上和有的文章里是分為兩種,沒有簡單工廠。 工廠模式主要的作用是:一個對象在實例化的時候可以選擇多個類,在實例化的時候根據一些業務規則來選擇實例化哪個類。 1.先定義一個圖形介面,有一個畫方法 2.再創建兩個實現介面的實體類 3.創 ...
  • 創建模式——工廠方法 本文解決上需要修改簡單工廠的問題,增加東西,工廠要改代碼。 簡單來說就是將工廠變成介面: 現在User類裡面會變成如下: 但是需要瞭解工廠的類,不符合迪米特法則,而且每個產品一個工廠很麻煩,之後就要用到抽象工廠。 ...
  • 1.創建模式:簡單工廠 單例模式: ArticleDao介面: public interface ArticleDao { public void addArticle(String article) ; public void delArticle(int id) ; public void up ...
  • 1. 例子 1. 做一個鴨子模擬器,裡面有很多不同的鴨子,有的可以游泳,有的可以睡覺,有的可以呱呱叫,一般套路是定義一個鴨子的超類,在 超類里定義睡覺,游泳,呱呱叫的方法,再讓不同的鴨子子類繼承這個超類,實現自己的display()方法來表現鴨子的行為,像下麵這樣: 2. 但如果要加一個可以吃火鍋的 ...
  • 系統架構設計師-軟體水平考試高級-理論-資料庫。其中涉及資料庫模式(三級抽象,兩層映射),數據模型,關係代數,規範化理論,事務處理等。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...