設計模式 - 原型模式

原型模式(Prototype),在制造業種通常是指大批量生產開始之前研發出的概念模型,并基于各種參數指標對其進行檢驗,效果達到了質量要求,即可參照這個原型進行批量生產。即,原型模式可以用對象創建對象,而不是用類創建對象,以此達到效率的提升。

舉個栗子,類似于打印機和復印機的區別:

  • 第一份打印出來的原文稿,我們稱之為“原型文件”
  • 對于復印過程,我們稱之為“原型拷貝”

原型模式對于 非常復雜初始化過程的對象,或者是?需要消耗大量資源?的情況下,原型模式是更好的選擇。

目錄

一、以空戰游戲為例

1. 敵機類?EnemyPlane 代碼:

2. 怎樣創建500臺敵機

1)??for循環批量生產敵機

2)??懶加載依然有性能問題

3)??細胞分裂

3. 復雜對象的克隆?- 深拷貝、淺拷貝

4. 克隆的本質


一、以空戰游戲為例

假設我們設計一個空戰游戲的程序,

為了簡單,我們設定游戲為單打,也就是說主角飛機只有一駕,而敵機有很多駕,而且可以在屏幕上垂直向下移動來撞擊注解飛機,具體是怎樣實現的呢?其實比較簡單,就是程序不停改變坐標并且在畫面上重繪而已。由淺入深,我們?先試著寫一個敵機類

Tips:空戰游戲中的主角如果是單個實例的話,其實就用到單例模式了。可以參考:?設計模式 - 單例模式-CSDN博客,本文只關注可以有多個實例的敵機。

1. 敵機類?EnemyPlane 代碼:

public class EnemyPlane {private int x; // 敵機橫坐標private int y = 0; // 敵機縱坐標public EnemyPlane(int x) { // 構造器this.x = x;}public int getX() {return x;}public int getY() {return y;}public void fly() { // 讓敵機飛y++; // 每調用一次,敵機飛行時縱坐標+1}
}

縱坐標固定為0,由于敵機一開始是從頂部飛出去的。

只有getter沒有setter,也就是只能在初始化時確定好敵機的橫坐標x,之后不允許改了


2. 怎樣創建500臺敵機

我們想讓敵機向雨點一樣不斷下落,首先需要實例化500駕敵機。

1)??for循環批量生產敵機

這樣做法看似沒有問題,實際上效率非常低。

游戲畫面不可能同時出現500駕敵機,而且在游戲未開始的時候就加載了500駕,不僅使加載速度變慢、也是對有限內存資源的一種浪費

public class Client {public static void main(String[] args) {List<EnemyPlane> enemyPlanes = new ArrayList<EnemyPlane>();for (int i = 0; i < 500; i++) {// 此處于隨機縱坐標處出現敵機EnemyPlane ep = new EnemyPlane(new Random().nextInt(200));enemyPlanes.add(ep);}}
}

那么,到底什么時候才去構造敵機,-- 當然是懶加載了

按照地圖坐標,屏幕滾動到某一點時才實時構造敵機,就解決問題了。

2)??懶加載依然有性能問題

主要原因在于,“new” 關鍵字進行的基于類的實例化過程,每駕敵機都進行全新構造的做法是不合適的,其代價是耗費更多的CPU資源。

尤其大型游戲中,很多個線程不停運轉著,CPU資源本身就非常寶貴,此時如果進行大量的類構造與復雜的初始化工作,必然會造成游戲卡頓、甚至會造成系統無響應

3)??細胞分裂

硬件永遠離不開優秀的軟件,我們絕不允許以糟糕的軟件設計對硬件發起挑戰。

既然循環第一次之后已經實例化好了一個敵機原型,那么之后又何必去重復這個構造過程呢?敵機對象是否能像細胞分裂一樣自我復制呢?要解決這個問題,原型模式是最好的解決方案了。

1)重構敵機類,支持原型拷貝

讓敵機類EnemyPlane實現了java.lang包中的克隆接口Cloneable,并在實現方法中調用了父類Object的克隆方法,省去了由類而生的再造過程。

public class EnemyPlane implements Cloneable {private int x; // 敵機橫坐標private int y = 0; // 敵機縱坐標public EnemyPlane(int x) { // 構造器this.x = x;}public int getX() {return x;}public int getY() {return y;}public void fly() { // 讓敵機飛y++; // 每調用一次,敵機飛行時縱坐標+1}// 此處開放setX,是為了讓克隆后的實例重新修改橫坐標public void setX(int x) {this.x = x;}// 重寫克隆方法@Overridepublic EnemyPlane clone() throws CloneNotSupportedException {return (EnemyPlane)super.clone();}
}

至此,克隆模式其實已經實現了,只需簡單的調用克隆方法即可更高效地得到一個全新的實例副本。

為了更方便的生產飛機,我們決定定義一個敵機克隆工廠類

public class EnemyPlaneFactory {// 此處用單例模式創建一個敵機原型private static EnemyPlane protoType = new EnemyPlane(200);// 獲取敵機克隆實例public static EnemyPlane getInstance(int x) {EnemyPlane clone = protoType.clone(); // 復制原型機clone.setX(x); // 重新設置克隆機的x坐標return clone;}
}

我們在敵機克隆工廠類EnemyPlaneFactory中第4行使用了一個靜態的敵機對象作為原型,其中獲取敵機實例的方法getInstance(),其簡單的調用克隆方法得到了一個新的克隆對象(此處省略了一場捕獲代碼),并將其橫坐標重設為傳入的參數,最后返回此克隆對象,這樣我們便可以輕松獲取一駕敵機的克隆實例了

敵機克隆工廠類定義完畢,客戶端代碼就留給讀者自己實踐了。

但需要注意,一定要使用懶加載方式,如此既可以節省內存空間,又可以確保敵機的實例化速度,實現敵機的即時性按需克隆,這樣游戲便再也不會出現卡頓現象了。


3. 復雜對象的克隆?- 深拷貝、淺拷貝

最后,在使用原型模式之前,必須搞清楚深拷貝與淺拷貝這兩個概念,否則會對復雜對象的克隆感到無比困惑

假設,敵機類里有一顆子彈可以發射并擊殺玩家的飛機,那么敵機中則包含一顆實例化好的子彈對象,請參考代碼清單:

public class EnemyPlane implements Cloneable {private Bullet bullet = new Bullet();private int x; // 敵機橫坐標private int y = 0; // 敵機縱坐標// 之后代碼省略……
}

如上代碼,此時如果進行克隆操作,能否將子彈對象一起成功克隆呢?

答案是否定的:

  • Java中的變量分為原始類型和引用類型,淺拷貝指只復制原始類型的值。而引用類型也會被拷貝,但是這個操作知識拷貝了引用類型的地址引用(指針),也就是說副本敵機與原型敵機中的子彈是同一顆,因為兩個同樣的地址實際指向的內存對象是同一個bullet對象。
  • 需要注意的是,克隆方法中調用父類Objecrt的clone方法進行的是淺拷貝,所以此處的bullet并沒有真正克隆。
public class EnemyPlane implements Cloneable {private Bullet bullet;private int x; // 敵機橫坐標private int y = 0; // 敵機縱坐標public EnemyPlane(int x, Bullet bullet) {this.x = x;this.bullet = bullet;}@Overrideprotected EnemyPlane clone() throws CloneNotSupportedException {EnemyPlane clonePlane = (EnemyPlane) super.clone(); // 克隆出敵機clonePlane.setBullet(this.bullet.clone()); // 對子彈進行深拷貝return clonePlane;}// 之后代碼省略……
}

如上代碼顯示,首先clone方法中依舊對敵機對象進行克隆操作,緊接著對敵機子彈bullet也進行了克隆,這個就是深拷貝操作。當然,此處要注意對于子彈類Bullet同樣也得實現克隆接口,請讀者自行實現,此處就不再贅述了。

簡而言之:深拷貝會復制對象及其所有嵌套子對象,而淺拷貝只復制對象本身,嵌套子對象仍然引用原對象。


4. 克隆的本質

在使用克隆模式對游戲代碼反復重構后,游戲性能得到了極大的提升,流暢的游戲畫面確保了優秀的用戶體驗。最后,我們來看原型模式的類結構。

  • Prototype(原型接口)?:聲明克隆方法,對應本例程代碼中的Cloneable接口。
  • ConcretePrototype(原型實現)?:原型接口的實現類,實現方法中調用super. clone()即可得到新克隆的對象。
  • Client(客戶端)?:客戶端只需調用實現此接口的原型對象方法clone(),便可輕松地得到一個全新的實例對象。

從類到對象叫作“創建”?,而由本體對象至副本對象則叫作“克隆”?,當需要創建多個類似的復雜對象時,我們就可以考慮用原型模式。

究其本質,克隆操作時Java虛擬機會進行內存操作,直接拷貝原型對象數據流生成新的副本對象,絕不會拖泥帶水觸發一些多余的復雜操作(如類加載、實例化、初始化等),所以其效率遠遠高于“new”關鍵字所觸發的實例化操作。

-- 秒懂設計模式學習筆記

-- 原型

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/87748.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/87748.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/87748.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

MySQL數據庫基礎:從零開始的第一步【Linux】

前言 各位小伙伴們&#xff0c;好久不見&#xff01;近期&#xff0c;我的文章更新頻率確實有些緩慢&#xff0c;在此誠摯地向大家道歉。這個月是我的期末考試月&#xff0c;正處于緊張的復習&#xff08;也可以說是重新學習&#xff09;階段。盡管學業繁忙&#xff0c;但我依然…

502 Bad Gateway:服務器作為網關或代理時收到無效響應處理方式

502 Bad Gateway 錯誤是 Web 開發和服務器管理中常見的問題&#xff0c;通常表示網關或代理服務器收到無效響應。這種錯誤可能由多種原因引起&#xff0c;包括后端服務故障、網絡問題或配置錯誤等。了解502錯誤的原因及其處理方式&#xff0c;對于維護網站的可用性和用戶體驗至…

Abel 變換,離散型分部積分

文章目錄 零、引入&#xff1a;分部積分一、Abel 變換1.1 Abel 變換1.2 證明 二、一些比較淺顯的應用2.1 等差 乘 等比型求和2.2 平方求和公式2.3 不等式證明 三、一些算法題的式子優化3.1 3500.將數組分割為子數組的最小代價3.2 D. Array Splitting3.3 300. 任務安排1 零、引入…

火山 RTC 引擎12----合流轉推 集成

一、火山、網易 合流轉推集成 1、 首次先要startPush,要不然,推不了流 void NRTCEngine::PushToCDN(std::string taskID, std::string url) {if (m_video == nullptr) return;bytertc::IMixedStreamConfig* config = getMixedStreamConfig(url);int ret = m_video->star…

基于STM32設計的物聯網疫苗冷鏈物流監測系統

文章目錄 一、前言1.1 項目介紹【1】項目開發背景【2】設計實現的功能【3】項目硬件模塊組成【4】設計意義【5】國內外研究現狀(1)國內研究現狀(2)國外研究現狀(3)技術演進趨勢分析(4)現存技術缺口(5)關鍵案例技術對比表【6】摘要1.2 設計思路1.3 系統功能總結1.4 開…

音頻中采樣率和幀是什么?怎么理解?

視頻中的“幀”是指一張圖片&#xff0c;那么在音頻中&#xff0c;“幀”的含義就完全不同了。理解音頻中的“幀”概念&#xff0c;對做音視頻處理、流媒體開發非常關鍵。 一、聲音是怎么采集的&#xff1f; 音頻采集是指通過麥克風等設備捕捉周圍環境中的聲波&#xff0c;并…

第三方檢測護航軟件登記:企業合規的技術通行證與市場信任基石

一、軟件產品登記測試&#xff1a;合規化的必經之路 根據《軟件產品管理辦法》&#xff0c;所有上市軟件必須通過第三方檢測機構的專業評估&#xff0c;確保功能、性能、安全性等指標符合國家標準&#xff08;如GB/T 25000系列&#xff09;。這一強制性要求不僅規避了法律風險…

產品頁不被收錄的6個技術原因(非重復內容/爬蟲限制類)

頁面未被收錄的原因可能藏在代碼架構或服務器配置中 比如爬蟲無法“看懂”你的動態內容&#xff0c;或是某個參數設置錯誤導致頁面被判定為重復。 本文從技術排查角度出發&#xff0c;整理6個最易被忽視但直接影響收錄的實操問題。 頁面加載速度拖慢爬蟲抓取 例如&#xff0…

如何在FastAPI中打造一個既安全又靈活的權限管理系統?

title: 如何在FastAPI中打造一個既安全又靈活的權限管理系統? date: 2025/06/16 08:17:05 updated: 2025/06/16 08:17:05 author: cmdragon excerpt: FastAPI權限系統通過依賴注入實現三級驗證:身份認證、角色驗證和權限校驗。數據庫模型包括用戶、角色和權限注冊表,支持…

通過Radius認證服務器實現飛塔/華為防火墻二次認證:原理、實踐與安全價值解析

引言&#xff1a;數字化轉型中的身份認證挑戰 在數字化轉型加速的今天&#xff0c;企業網絡邊界日益模糊&#xff0c;混合云架構、遠程辦公、物聯網設備接入等場景對網絡安全提出全新挑戰。傳統防火墻基于IP/端口的訪問控制已無法滿足動態安全需求&#xff0c;如何構建"持…

golang--context的使用指南與核心特性

Go 語言 context 包&#xff1a;使用指南與核心特性 一、context 的本質與設計目的 context 是 Go 語言中管理請求生命周期的核心機制&#xff0c;它提供了一套統一的方式來&#xff1a; 傳遞請求范圍數據&#xff08;如用戶認證信息&#xff09;控制跨 goroutine 的生命周期…

耗時3小時,把這兩天做好的爬蟲程序,用Python封裝成exe文件

先執行命令如下&#xff1a; pip install pyinstaller py -m PyInstaller --log-levelDEBUG --add-data "config.ini;." nmpa_gui.py很快在dist目錄下就有生成一個nmpa_gui文件夾&#xff0c;運行 nmpa_gui.exe&#xff0c;報錯&#xff1a; 1??初始化爬蟲… 程序…

Linux下nginx訪問路徑頁面

第一步&#xff1a;通過Xshell在虛擬機中下載nginx sudo apt-get install nginx 第二步&#xff1a;進入nginx配置頁面 cd /etc/nginx 我這里創建了一個html文件夾 在進入去創建頁面并且重新加載 boahuboahu-VMware-Virtual-Platform:/$ cd /etc/nginx boahuboahu-VMware-Vir…

三維視頻融合怎么弄?三步實現精準投射與自由修剪

分享大綱&#xff1a; 1、場景引入&#xff1a;為什么你的三維場景視頻融合效果不理想&#xff1f; 2、解決方案&#xff1a;捷碼視頻融合三步操作指南 3、捷碼平臺&#xff1a;低代碼構建動態三維視界 在智慧城市中的安防領域&#xff0c;將實時視頻與三維場景融合已是大勢需求…

探索阿里云網絡與CDN產品:解鎖高效網絡體驗

阿里云網絡產品概述 在云計算蓬勃發展的當下&#xff0c;網絡作為連接計算、存儲與用戶的關鍵紐帶&#xff0c;其重要性不言而喻。阿里云作為全球知名的云計算服務提供商&#xff0c;憑借其豐富且強大的網絡產品體系&#xff0c;為企業數字化轉型筑牢了堅實的網絡根基&#xf…

深入理解C語言指針(二):從數組到多級指針的全面解析

作為C語言的核心概念&#xff0c;指針常常讓初學者感到困惑。本文將從數組與指針的關系入手&#xff0c;逐步揭開指針在數組操作、函數傳參以及多級指針中的神秘面紗&#xff0c;幫助你建立系統的指針知識體系。 一、數組名的雙重身份&#xff1a;首地址與整體標識 在C語言中&a…

Windows PPT/word怎么pdf不降低分辨率,插入可編輯

Windows PPT/word怎么pdf不降低分辨率 下載軟件Inkscape&#xff1a;Inkscape - Draw Freely. | Inkscape 然后使用Inkscape將你的PDF轉為svg, 然后用office的PPT打開&#xff0c;將svg復制進PPT/word&#xff0c;然后保存就可以了 插入可編輯的&#xff08;只能通過Mac的才可…

vue3 select 選中值時,即獲得id,也獲得name值并且輸入框正確選中

1.獲取 name和id 直接綁定對象 將 value 綁定為整個對象&#xff0c;通過 change 事件獲取完整數據 value-key 綁定唯一標識 value 綁定為整個對象&#xff0c;通過 change 事件獲取完整數據 <el-select v-model"selectedItem" change"handleChange"…

什么是Seata

Seata的實現原理主要圍繞其核心架構&#xff08;TC/TM/RM&#xff09;和事務模式&#xff08;如AT、TCC等&#xff09;展開&#xff0c;通過協調全局事務與分支事務的協作保證數據一致性。以下是核心實現原理的詳細解析&#xff1a; ?? ??一、核心架構協作機制?? Seata通…

linux ARM64架構用戶空間和內核空間的區分

一、ARM64 架構地址空間的「黃金分割」 ARM64&#xff08;ARMv8-A&#xff09;采用 48 位虛擬地址&#xff08;Linux 默認配置&#xff09;&#xff0c;總空間為 256TB&#xff0c;分為高低兩個 128TB 區域&#xff1a; 1. 地址空間整體布局 虛擬地址空間&#xff08;48位&a…