設計模式(Java語言)- 原型模式

来源:https://www.cnblogs.com/rainple/archive/2020/05/05/12833684.html
-Advertisement-
Play Games

原型模式(Prototype Pattern)也有人將原型模式稱為克隆模式,是屬於創造型設計模式,用於創建重覆的對象,提供了一種創建對象的最佳方式。原型模式需要實現Cloneable介面,來實現對象的克隆。在實際的應用中,如果應用需要反覆創建相同的對象時,並且創建這個對象需要花費大量時間或者需要訪問 ...


  原型模式(Prototype Pattern)也有人將原型模式稱為克隆模式,是屬於創造型設計模式,用於創建重覆的對象,提供了一種創建對象的最佳方式。原型模式需要實現Cloneable介面,來實現對象的克隆。在實際的應用中,如果應用需要反覆創建相同的對象時,並且創建這個對象需要花費大量時間或者需要訪問許可權,比如需要讀取資料庫,配置文件等,如果每次創建重覆對象都需要讀一次資料庫,那麼這種方式顯然並不是高效的。這時可以考慮使用原型模式來解決,提高效率,此時只需要在創建原型對象時需要讀取一次資料庫或配置文件等,當後面需要需要創建這個對象時只需要從原型對象克隆一個出來即可。另外,原型模式也解決了構建複雜對象時繁瑣的過程,原型模式不關心對象創建的細節,用戶只需要調用克隆的方法就可以創建出一個一摸一樣的對象,簡化創建流程。

  既然原型模式也成為克隆模式,那麼對象複製過程必然用到Java的克隆方法。所以你也需要瞭解什麼是淺克隆和深克隆。

  淺克隆

  淺克隆複製的是對象基本類型的屬性,對於引用類型的屬性,淺克隆置複製該應用類型的地址,因為克隆對象的被克隆對象的應用類型屬性是同一個記憶體地址,即為同一個對象,所以在修改其中一個對象的該屬性時,另一個對象的改屬性也會被修改,很容易將原型對象屬性修改,這也是在使用原型模式時需要註意的地方。淺克隆在代碼中的實現也比較簡單,Java語言中本身就已經提供相關的介面和方法了,我們在使用時只需要繼承Cloneable介面,重寫clone方法即可實現對象的淺克隆。代碼實現如下:

public class Sheep implements Cloneable {

    private String name;
    private Color color = new Color();

    public Sheep() {
    }

    public Sheep(String name, String color) {
        this.name = name;

        setColor(color);
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", color=" + color +
                '}';
    }

    public Color getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color.setColor(color);
    }

    public String getName() {
        return name;
    }

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

  

public class Color implements Cloneable {

    private String color;

    public Color() {
    }

    public Color(String color) {
        this.color = color;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Color{" +
                "color='" + color + '\'' +
                '}';
    }
}

  測試

public class Test {

public static void main(String[] args) throws CloneNotSupportedException {
Sheep test = new Sheep("test","白色");
System.out.println(test);
Sheep clone = (Sheep) test.clone();
clone.setColor("黑色");
clone.setName("test01");
System.out.println(test);
System.out.println(clone);
}

}

  運行程式時控制台列印出了:

  Sheep{name='test', color=Color{color='白色'}}

  Sheep{name='test', color=Color{color='黑色'}}

  Sheep{name='test01', color=Color{color='黑色'}}

  很顯然,test對象創建時是白色的,然後用這個對象進行克隆得到 clone 實例,然後將clone 對象的顏色修改成黑色,name修改成test01,最終兩個對象的顏色都變成了黑色,印證了上面說的話,對於引用類型克隆的是對象的記憶體地址。可能會有人好奇,String也是引用類型,為什麼克隆對象修改了name屬性,原型對象卻沒有被修改了?這是因為String是final類型,克隆過程中自然會是兩個不同的記憶體地址。

 

  深克隆

  深克隆和淺克隆的區別在於,深克隆時引用類型屬性複製的是該屬性的值,與原型對象的擁有不同的記憶體地址,即兩個是不同的對象,他們任意一個改屬性值都不會影響到彼此。深克隆的實現方式有兩種,第一種,實現Cloneable介面,重寫clone方法,與淺克隆不同的是多一步將引用類型的變數再調用一次改變數的clone方法。不推薦用這種方法實現深克隆,每次修改對象的變數時都需要修改一次clone方法,違反了ocp原則。第二種,利用Java序列化與發序列化來實現,推薦使用這種方式。

  代碼實現:

public class Color implements Cloneable, Serializable {

    private String color;

    public Color() {
    }

    public Color(String color) {
        this.color = color;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Color{" +
                "color='" + color + '\'' +
                '}';
    }
}

  

public class Sheep implements Cloneable, Serializable {

    private String name;
    private Color color = new Color();

    public Sheep() {
    }

    public Sheep(String name, String color) {
        this.name = name;

        setColor(color);
    }

    /**
     * 利用序列化與反序列化實現深克隆
     * @return
     */
    public Object deepClone() {
        ByteArrayInputStream bis = null;
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ObjectInputStream ois = null;
        try {
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);


            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            return ois.readObject();
        }catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if (ois != null) {
                    ois.close();
                }
                if (bis != null) {
                    bis.close();
                }
                if (oos != null) {
                    oos.close();
                }
                if (bos != null) {
                    bos.close();
                }
            }catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Sheep clone = (Sheep) super.clone();
        clone.color = (Color) clone.color.clone();
        return clone;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", color=" + color +
                '}';
    }

    public Color getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color.setColor(color);
    }

    public String getName() {
        return name;
    }

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

  註意,如果使用Java的序列化與反序列化,則改對象需要實現Serializable介面,否則會拋序列化異常。

  

  總結

  1、原型模式有兩種實現方式,第一種利用Object類中的clone方式,重寫Cloneable的clone方法,淺克隆時直接調用Object類提供的clone方式即可。深克隆則需要再調用需要被克隆的對象的clone方法,當然該對象也必須實現Cloneable介面。第二種方式是利用Java的序列化和反序列化技術,這種方式也有一個缺點是所有需要序列化的變數都必須要實現Serializable介面。

  2、原型模式的優點:提高效率;屏蔽複雜的對象構建過程,簡化代碼。

  3、原型模式的缺點:

    1)配備克隆方法需要對類的功能進行通盤考慮,這對於全新的類不是很難,但對於已有的類不一定很容易,特別當一個類引用不支持串列化的間接對象,或者引用含有迴圈結構的時候。

    2)必須實現 Cloneable 介面或Serializable介面。

  4、原型模式的應用場景:

    1)資源優化場景。

    2)對象初始化需要大量的資源,包括數據,硬體資源等。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 完成頁面定時彈出廣告 需求分析 ​ 一般網頁,當我們剛打開的時候,它會5秒之後,顯示一個廣告,讓我們看5秒鐘,然後他的廣告就自動消失了! 技術分析 定時器 setInterval : 每隔多少毫秒執行一次函數 setTimeout: 多少毫秒之後執行一次函數 clearInterval clearT ...
  • 使用JS完成圖片的輪播效果 需求分析 在我們的網站首頁,通常需要有一塊區域,用來顯示廣告,但是這塊區域如果僅僅顯示一張圖片肯定是不夠的, 故我們需要採用動態迴圈播放我們所有的廣告. 顯示效果照抄黑馬程式員的網站首頁 技術分析 切換圖片: 每個三秒鐘做一件事: window.setInterval() ...
  • 使用JS完成簡單的數據校驗 需求分析 使用JS完成對註冊頁面的簡單數據校驗,不允許出現用戶名或密碼為空的情況 技術分析 from表單屬性——onsubmit必須要有返回值,若為true,submit提交成功,若為false,無法提交 JS方法 :變數的值 :變數的長度 :檢驗括弧內的值 正則表達式 ...
  • 使用DIV+CSS完成註冊頁面的優化 需求分析 由於我們的註冊頁面也是用table佈局的,存在與首頁同樣的問題,所以我們需要使用div+css對我們的註冊頁面進行美化 技術分析 CSS的盒子模型: 萬物皆盒子 內邊距: padding top padding right padding bottom ...
  • 使用CSS完成網站首頁的優化 需求分析 由於我們昨天使用表格佈局存在缺陷,那麼我們要來考慮使用DIV+CSS來對頁面進行優化 表格佈局的缺陷 1. 嵌套層級太多, 一旦出現嵌套順序錯亂, 整個頁面達不到預期效果 2. 採用表格佈局,頁面不夠靈活, 動其中某一塊,整個表格佈局的結構全都要變 技術分析 ...
  • 網站註冊頁面案例 需求分析 編寫一個HTML頁面, 顯示效果如圖所示 技術分析 表單標簽 action : 直接提交的地址 method : get 方式 預設提交方式 ,會將參數拼接在鏈接後面 , 有大小限制 ,4k post 方式 會將參數封裝在請求體中, 沒有這樣的限制 input : typ ...
  • 網站架構變遷 Intro 從最早的 html 的學習到現在從單體應用遷移到微服務架構,所經歷的網站架構也一直在變化,於是想寫一篇關於網站架構變遷的文章。 單伺服器 最早的我們的網站只有一臺伺服器,網站應用 + 資料庫 + 網站文件 都在同一臺伺服器上,有的時候一臺伺服器上也會有多個網站。 這個階段的 ...
  • 當程式運行出現異常時,會退出程式結束運行而不至於讓程式崩潰。 1. 異常類 所有異常的根類是java.lang.Throwable,其下有兩個子類:Error和Exception。 (1) Error Error是程式無法處理的錯誤,錶面系統JVM處於不可恢復的崩潰狀態,此時錯誤與代碼書寫無關。 如 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...