軟體設計模式系列之七——原型模式

来源:https://www.cnblogs.com/coodream2009/archive/2023/09/17/17707672.html
-Advertisement-
Play Games

系統設計之緩存五種策略 當我們在架構中引入緩存時,緩存和資料庫之間的同步就變得不可避免。 讓我們看看如何保持數據同步的五種常見策略。 1)閱讀策略: 緩存在一邊 通讀2)寫策略:寫周圍 回信 寫通緩存策略經常組合使用。例如,write-around 通常與 cache-aside 一起使用,以確保緩 ...


1 模式的定義

原型模式(Prototype Pattern)是一種創建型設計模式,其主要目的是通過複製現有對象來創建新對象,而不是使用構造函數。原型模式將對象的創建委托給原型對象,通過克隆(複製)來生成新對象,這種方式可以避免對象的重覆初始化,提高性能,並使對象的創建更加靈活和動態。

原型模式的關鍵思想是通過複製已有對象的屬性和狀態來創建新的對象,這種方式避免了每次都使用構造函數初始化對象,特別適用於對象創建過程複雜、耗時或需要動態配置的情況。

2 舉例說明

原型模式在日常生活中的一個常見示例是使用複印機來複制文件或文檔。如果你需要複製一份文件,一般情況下不會手工重新編寫該文件的每個字,而是使用複印機來製作副本。在這裡,原文件充當原型,而複印機則是用於創建新文件副本的工具。使用複印機來複制文件,通過克隆原文件來創建新文件副本,從而節省時間和工作量。

還有一個例子就是在西游記中,孫悟空用自己的猴毛變成很多新的孫悟空,也可以看作孫悟空用自己做原型,拷貝出相同的猴子。

這些例子都有助於更好地理解原型模式的概念和應用。

3 結構

原型模式的結構包括以下要素:

抽象原型介面(Prototype):這是一個抽象介面或抽象類,它聲明瞭一個克隆方法(通常命名為 clone() 或類似的名稱)。這個方法用於複製當前對象並返回一個新的副本。所有具體原型類都必須實現這個介面或繼承這個抽象類,以確保它們能夠被克隆。

具體原型類(Concrete Prototype):這些是實際的對象類,它們實現了抽象原型介面,並提供了自己的克隆方法。具體原型類通常包含對象的屬性和方法。當客戶端需要創建新對象時,它們通過調用克隆方法來複制現有對象。

客戶端(Client):客戶端代碼是使用原型模式的地方,它通過調用具體原型類的克隆方法來創建新對象。客戶端不需要瞭解對象的具體構造方式,只需知道如何複製對象。

以下是原型模式的典型結構示意圖

在這個結構中,抽象原型介面定義了克隆方法,具體原型類實現了克隆方法,並可以包含其他屬性和方法。客戶端代碼通過克隆方法創建新對象,而不必關心對象的具體構造細節。這種結構使得對象的創建更加靈活和可維護。

4 實現步驟

實現原型模式的關鍵步驟包括以下幾個:

創建抽象原型介面(Prototype):首先,創建一個抽象原型介面或抽象類,其中包含一個克隆方法(通常命名為 clone() 或類似的名稱),用於複製當前對象並返回一個新的副本。這個介面將規範所有具體原型類必須實現的方法。

創建具體原型類(Concrete Prototype):對於每個需要被克隆的具體對象類型,創建一個具體原型類,它實現了抽象原型介面,並提供了自己的克隆方法。在克隆方法中,通常會創建一個新的對象,將當前對象的屬性值複製給新對象,並返回新對象。

客戶端使用原型對象:在客戶端代碼中,當需要創建新對象時,不直接使用構造函數,而是通過克隆已有的原型對象來創建新對象。客戶端代碼通常只需要知道如何調用原型對象的克隆方法,而無需瞭解對象的具體構造細節。

克隆方法的實現:在具體原型類中,克隆方法的具體實現取決於對象的類型和屬性。如果對象包含引用類型的成員變數,需要考慮深度克隆以確保對象的所有狀態都被正確複製。

測試和驗證:在客戶端代碼中測試原型模式,確保克隆的對象與原始對象在屬性和行為上一致。

5 代碼實現

以下是一個通用的原型模式實現步驟示例(使用Java):

// 1. 創建抽象原型介面
interface Prototype {
    Prototype clone();
}

// 2. 創建具體原型類
class ConcretePrototype implements Prototype {
    private String field;

    public ConcretePrototype(String field) {
        this.field = field;
    }

    @Override
    public Prototype clone() {
        return new ConcretePrototype(this.field);
    }

    public void setField(String field) {
        this.field = field;
    }

    public String getField() {
        return field;
    }
}

// 3. 客戶端使用原型對象
public class Client {
    public static void main(String[] args) {
        // 創建原型對象
        Prototype original = new ConcretePrototype("Original Field");

        // 克隆原型對象來創建新對象
        Prototype clone = original.clone();

        // 驗證新對象的屬性與原始對象相同
        System.out.println("Original Field: " + original.getField());
        System.out.println("Clone Field: " + clone.getField());
    }
}

在這個示例中,抽象原型介面定義了克隆方法,具體原型類實現了該介面並提供了自己的克隆方法。客戶端通過克隆方法創建新對象,驗證新對象的屬性與原始對象相同。這個示例展示了原型模式的基本實現步驟。

6 典型應用場景

原型模式在以下情況下是典型的應用場景:

需要創建對象的成本較高:當對象的創建和初始化成本較高時,原型模式可以顯著提高性能。每次都使用構造函數創建對象可能會導致不必要的開銷,因此通過複製已有對象來創建新對象更為高效。

在電腦游戲中,創建和初始化複雜的游戲角色可能需要大量時間和資源。如果游戲需要大量相似的角色,可以使用原型模式來複制現有角色,節省創建時間。

對象的屬性變化頻繁:當對象的屬性需要經常變化,但你希望保持對象的初始狀態作為基礎,可以使用原型模式。這樣,你可以創建一個原型對象,並根據需要克隆它來創建新的對象。

在圖形設計工具中,用戶可以創建和編輯圖形對象,如圖形文本框。原始對象可以充當原型,用戶可以複製它來創建多個類似但具有不同文本內容的圖形文本框。

動態配置對象:當對象的屬性需要根據運行時配置或用戶輸入而變化時,原型模式很有用。你可以創建一個原型對象,然後根據需要修改其屬性,而無需重新構建對象。

在網站創建工具中,用戶可以創建網頁並自定義顏色、字體、佈局等屬性。原始網頁對象可以作為原型,用戶可以克隆它並根據自己的需求修改屬性。

保護性拷貝:原型模式可以用於創建對象的深拷貝,以保護原始對象免受外部修改的影響。這對於涉及敏感數據或狀態的對象非常有用。

在安全敏感的應用程式中,用戶身份驗證對象可能包含用戶的敏感信息。通過使用原型模式創建深拷貝,可以確保不會在外部修改原始對象的敏感數據。

總之,原型模式適用於需要創建對象的成本高、屬性變化頻繁、動態配置或需要保護性拷貝的場景。它提供了一種高效、靈活和可維護的方式來創建對象,併在許多領域中有廣泛的應用。

7 優缺點

優點:
提高性能:避免了對象的重覆初始化,提高了對象創建的效率。

簡化對象創建:客戶端代碼可以通過克隆來創建新對象,無需瞭解對象的具體構造方式。

動態配置對象:允許在運行時動態配置對象的屬性。

保護性拷貝:可以創建對象的深拷貝,保護原始對象免受修改的影響。

缺點:
需要實現克隆方法:每個具體原型類都需要實現克隆方法,這可能需要一些額外的工作。

淺克隆問題:預設的克隆操作是淺克隆,如果對象包含引用類型的成員變數,可能需要手動實現深克隆。

8 類似模式

在 Spring 框架中,使用了原型模式和單例模式來管理對象的創建和生命周期。

原型模式在 Spring 中的應用:

原型範圍(Scope)的 Bean:在 Spring 中,你可以將一個 Bean 配置為原型範圍,這意味著每次從 Spring 容器請求該 Bean 時,都會創建一個新的實例。這就是原型模式的應用,每次都克隆一個對象來創建新實例。這對於那些需要頻繁創建新對象的場景非常有用,因為每個請求都會得到一個全新的 Bean 實例,而不會共用狀態。

使用 prototype 作用域聲明 Bean:在 Spring 的配置文件或使用註解時,你可以明確將 Bean 的作用域聲明為 prototype,告訴 Spring 這個 Bean 是原型範圍的,從而使用原型模式來創建對象。

<bean id="myBean" class="com.example.MyBean" scope="prototype">
</bean>

單例模式在 Spring 中的應用:

單例範圍(Scope)的 Bean:預設情況下,Spring 中的 Bean 是單例範圍的,這意味著 Spring 容器只會創建一個 Bean 的實例,併在整個應用程式中共用這個實例。這就是單例模式的應用,確保一個類只有一個實例。

使用 singleton 作用域聲明 Bean:雖然預設是單例範圍,但你也可以顯式將 Bean 的作用域聲明為 singleton,以明確表達這一點。

<bean id="myBean" class="com.example.MyBean" scope="singleton">
</bean>

在 Spring 中,原型模式和單例模式的選擇取決於對象的生命周期和狀態需求。如果你需要一個共用狀態的單一實例,可以使用單例模式。如果需要每次請求都獲得一個全新的對象實例,可以使用原型模式。Spring 提供了這兩種範圍的支持,以滿足不同的業務需求。

9 小結

原型模式是一種用於創建對象的設計模式,它通過克隆現有對象來創建新對象,從而提高性能、簡化對象創建和支持動態配置對象的需求。原型模式在需要頻繁創建對象,或者需要保護對象不受修改影響的情況下非常有用。


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

-Advertisement-
Play Games
更多相關文章
  • 1.分組 group by 詳情見,發佈的第七篇博客文章,7- MySQL函數 2.排序 order by 說明:在MySQL中,ORDER BY是一種用於對查詢結果進行排序的關鍵字。它可以根據一列或多列的值,以升序或降序的方式對查詢結果進行排序,使得查詢者可以更加方便 地查看、分析和處理數據。 使 ...
  • 1.分組group by 在MySQL中,GROUP BY的意思是“分組查詢”,它可以根據一個或多個欄位對查詢結果進行分組。 GROUP BY的作用是通過一定的規則將一個數據集劃分成若幹個小的區域,然後針對若幹個小區域進行數據處理。這可以理解為將數據按照某個欄位或者多個欄位進行分組。 使用GROUP ...
  • MySQL資料庫管理 資料庫-->數據表-->行(記錄):用來描述一個對象的信息 列(欄位):用來描述對象的一個屬性 常用的數據類型: int :整型 無符號[0,2^32-1],有符號[-2^31,2^31-1] float :單精度浮點 4位元組32位 double :雙精度浮點 8位元組64位 c ...
  • 在MySQL中,高級查詢是指使用更複雜的查詢語句和操作符來檢索和操作資料庫中的數據。高級查詢可以幫助您更精確地找到所需的信息,並提高查詢的效率和靈活性。 以下是高級查詢的一些常見應用場景和意義: 連接多個表:使用JOIN操作符將多個表連接起來,以便在一次查詢中獲取相關聯的數據。這對於在多個表之間建立 ...
  • 背景: 隨著項目體量越來越大,用戶群體越來越多,用戶的聲音也越來越明顯;關於應用發版之後用戶無感知,導致用戶用的是仍然還是老版本功能,除非用戶手動刷新,否則體驗不到最新的功能;這樣的體驗非常不好,於是我們團隊針對該問題給出了相應的解決方案來處理;技術棧:vue3+ts+vite+ant-design ...
  • 相比用戶停留時間短、用完即走的 Web 頁面,桌面 QQ 用戶在一次登錄後,可能會掛機一周以上,這段期間,如果沒有嚴格控制好 QQ 記憶體占用,那麼結果可能是用戶交互響應變慢、甚至 Crash。在系統監控工具里,高記憶體占用也會被直觀地反映出來,帶來不好的口碑。MAC QQ 灰度期間,也聽到了一些用戶關... ...
  • 介紹 ESLint 是一個根據方案識別並報告 ECMAScript/JavaScript 代碼問題的工具,其目的是使代碼風格更加一致並避免錯誤。在很多地方它都與 JSLint 和 JSHint 類似,除了: ESLint 使用 Espree 對 JavaScript 進行解析。 ESLint 在代碼 ...
  • 這是一個講解DDD落地的文章系列,作者是《實現領域驅動設計》的譯者滕雲。本文章系列以一個真實的並已成功上線的軟體項目——碼如雲(https://www.mryqr.com)為例,系統性地講解DDD在落地實施過程中的各種典型實踐,以及在面臨實際業務場景時的諸多取捨。 本系列包含以下文章: DDD入門 ...
一周排行
    -Advertisement-
    Play Games
  • WPF本身不支持直接的3D繪圖,但是它提供了一些用於實現3D效果的高級技術。 如果你想要在WPF中進行3D繪圖,你可以使用兩種主要的方法: WPF 3D:這是一種在WPF應用程式中創建3D圖形的方式。WPF 3D提供了一些基本的3D形狀(如立方體、球體和錐體)以及一些用於控制3D場景和對象的工具(如 ...
  • 一、XML概述 XML(可擴展標記語言)是一種用於描述數據的標記語言,旨在提供一種通用的方式來傳輸和存儲數據,特別是Web應用程式中經常使用的數據。XML並不預定義標記。因此,XML更加靈活,並且可以適用於廣泛的應用領域。 XML文檔由元素(element)、屬性(attribute)和內容(con ...
  • 從今年(2023)三月份開始,Github開始強制用戶開啟兩步驗證2FA(雙因數)登錄驗證,毫無疑問,是出於安全層面的考慮,畢竟Github賬號一旦被盜,所有代碼倉庫都會毀於一旦,關於雙因數登錄的必要性請參見:別讓你的伺服器(vps)淪為肉雞(ssh暴力破解),密鑰驗證、雙向因數登錄值得擁有。 雙因 ...
  • 第一題 下列代碼輸入什麼? public class Test { public static Test t1 = new Test(); { System.out.println("blockA"); } static { System.out.println("blockB"); } publi ...
  • 本文主要涉及的問題:用ElementTree和XPath讀寫XML文件;解決ElementTree新增元素後再寫入格式不統一的問題;QTableWidget單元格設置控制項 ...
  • QStandardItemModel 類作為標準模型,主打“類型通用”,前一篇水文中,老周還沒提到樹形結構的列表,本篇咱們就好好探討一下這貨。 還是老辦法,咱們先做示例,然後再聊知識點。下麵這個例子,使用 QTreeView 組件來顯示數據,使用的列表模型比較簡單,只有一列。 #include <Q ...
  • 一、直充內充(充值方式) 直充: 包裝套餐直接充值到上游API系統。【PID/Smart】 (如:支付寶、微信 話費/流量/語音/簡訊 等 充值系統)。 內充(套餐打包常見物聯卡系統功能): 套餐包裝 適用於不同類型套餐 如 流量、簡訊、語音 等。 (目前已完善流量邏輯) 二、套餐與計費產品 計費產 ...
  • 在前面幾天中,我們學習了Dart基礎語法、可迭代集合,它們是Flutter應用研發的基本功。今天,我們繼續學習Flutter應用另一個必須掌握知識點:非同步編程(即Future和async/await)。它類似於Java中的FutureTask、JavaScript中的Promise。它是後續Flut... ...
  • 針對改動範圍大、影響面廣的需求,我通常會問上線了最壞情況是什麼?應急預案是什麼?你帶開關了嗎?。當然開關也是有成本的,接下來本篇跟大家一起交流下高頻發佈支撐下的功能開關技術理論與實踐結合的點點滴滴。 ...
  • 1.d3.shuffle D3.shuffle() 方法用於將數組中的元素隨機排序。它使用 Fisher–Yates 洗牌演算法,該演算法是無偏的,具有最佳的漸近性能(線性時間和常數記憶體)。 D3.shuffle() 方法的語法如下: d3.shuffle(array, [start, end]) 其中 ...