【設計模式深度剖析】【5】【創建型】【原型模式】| 類比群發郵件,加深理解

👈?上一篇:建造者模式 ? ?|?? 下一篇:創建型設計模式對比👉?

目錄

  • 原型模式(Prototype Pattern)
    • 概覽
    • 定義
      • 英文原話
      • 直譯
    • 3個角色
      • 類圖
      • 1. 抽象原型(Prototype)角色
      • 2. 具體原型(Concrete Prototype)角色
      • 3. 客戶(Client)角色
      • 代碼示例
        • 1. 抽象原型
        • 2. 具體原型
        • 3. 被復制的對象的類
        • 4. 客戶端
        • 5. 測試類
    • 應用
      • 優點
      • 使用場景
    • 原型模式示例解析:郵件群發
      • 類圖
      • 1. 抽象原型角色:Prototype.java(接口定義克隆方法)
      • 2. 具體原型類:Mail.java
      • 3. 測試類
    • 補充知識點
      • Lombok的@Builder注解原理:建造者模式
        • 1. 先說下用法
        • 2. 原理分析

原型模式(Prototype Pattern)

>>本文源碼<<

概覽

  • 定義
  • 3個角色
    • 代碼示例
  • 應用
    • 優點
    • 使用場景
  • 案例分析
  • 原型模式示例解析:郵件群發

定義

英文原話

Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.

直譯

指定要使用原型實例創建的對象類型,并通過復制該原型創建新對象。

3個角色

類圖

Prototype

1. 抽象原型(Prototype)角色

為具體原型角色定義方法,指定統一標準

2. 具體原型(Concrete Prototype)角色

該角色是被復制的對象,必須實現抽象原型接口

Java中內置了克隆機制,Object類具有一個clone()方法,能夠實現對象的克隆,使一個類支持克隆需要以下兩步。

  • 實現Cloneable接口
  • 覆蓋Object的clone()方法,完成對象的克隆操作,通常只需要調用Object的clone()方法(“淺克隆”-即只復制關聯對象的引用)即可

3. 客戶(Client)角色

該角色提出創建對象的請求

代碼示例

>>示例源碼<<

1. 抽象原型

抽象原型Prototype接口繼承 Cloneable 接口,以標明該接口的實現類可以被復制,并聲明一個 clone()方法,該clone()方法是對Object類的clone()方法的重寫

package com.polaris.designpattern.list1.creational.pattern5.prototype.proto;/** 實現本接口,表明實現類可以被復制*/
public interface Prototype extends Cloneable {// 克隆方法Prototype clone();
}
2. 具體原型

具體原型ConcretePrototype實現clone()方法。這里調用的父類Object的clone()方法

package com.polaris.designpattern.list1.creational.pattern5.prototype.proto;/** 具體原型ConcretePrototype實現clone()方法*/
public class ConcretePrototype implements Prototype {@Overridepublic Prototype clone() {try {// 此處調用的Object類的clone()方法,并向下轉型為抽象原型類型。// Java中Object提供的clone()方法采用的是“淺”克隆,即只復制關聯對象的引用;// 而不復制關聯對象的數據。如果需要“深”克隆,則需要在覆蓋clone()方法時手動控制克隆的深度。return (Prototype) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();return null;}}
}
3. 被復制的對象的類

提供一個被復制的類,用于測試使用

package com.polaris.designpattern.list1.creational.pattern5.prototype.proto;import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;/** 被復制的對象的類*/
@Data
@Builder
@EqualsAndHashCode(callSuper = true)
public class User extends ConcretePrototype {private String name;private Integer age;
}

這里的@Builder是lombok的注解,加上該注解編譯出的class文件中,通過內部類的方式實現了建造者模式。便于用戶方便地創建對象。

(關于建造者模式看上一篇 建造者模式


@Builder的原理,參考補充知識點Lombok的@Builder注解原理:建造者模式

4. 客戶端

使用原型類的客戶類

public class Client {// 傳參傳入具體原型類示例,具體原型類實現了抽象原型的clone方法public Prototype operation(Prototype example) {// 得到example的副本Prototype p = example.clone();return p;}
}
5. 測試類

新建了一個用于被復制的user對象,

調用客戶類進行復制,傳入原型類型的對象(User類實現了原型類,通過繼承具體原型類實現的),然后返回一個原型實例,

之后測試了復制對象與原始對象屬性是同一對象,

然后對復制對象的屬性值通過反射進行了獲取與打印

package com.polaris.designpattern.list1.creational.pattern5.prototype.proto;import java.lang.reflect.Field;/*** @author Polaris 2024/5/17*/
public class DemoTest {public static void main(String[] args) {User user = User.builder().name("歷史").age(5000).build();Client client = new Client();Prototype clone = client.operation(user);System.out.println("name屬性是同一個:"+((User)clone).getName().equals(user.getName()));System.out.println("age屬性(>127,非讀緩存值)是同一個:"+((User)clone).getAge().equals(user.getAge()));System.out.println("-----------------");Field[] declaredFields = clone.getClass().getDeclaredFields();for (Field declaredField : declaredFields) {Object o;try {String name = declaredField.getName();declaredField.setAccessible(true);o = declaredField.get(clone);System.out.println(name + ":" + o);} catch (IllegalAccessException e) {e.printStackTrace();}}}
}/* Output:name屬性是同一個:true
age屬性(>127,非讀緩存值)是同一個:true
-----------------
name:歷史
age:5000*///~

輸出中:克隆前后的對象的屬性指向的是同一個對象。

也印證了:

Java中Object提供的clone()方法采用的是“淺”克隆,即只復制關聯對象的引用

應用

優點

原型模式的優點有以下幾個方面。

  1. 性能優良:原型模式是在內存二進制流的復制,要比直接new一個對象性能好,特別是在一個循環體內產生大量的對象時,原型模式可以更好地體現其優點。
  2. 逃避構造函數的約束:這既 是優點也是缺點,直接在內存中復制,構造函數是不會執行的,因此減少了約束,需要在實際應用時進行權衡考慮。

使用場景

原型模式的使用場景如下。

  1. 資源優化場景,類初始化需要消化非常多的資源,這個資源包括數據、硬件資源等。
  2. 性能和安全要求的場景,通過new產生一個對象需要非常煩瑣的數據準備或訪問權限,可以使用原型模式。
  3. 一個對象多個修改者的場景,一個對象需要提供給其他對象訪問,而且各個調用者可能都需要修改其值時,可以考慮使用原型模式復制多個對象供調用者使用。
  4. [注]:在實際項目中,原型模式很少單獨出現,一般是和工廠方法模式一起出現,通過clone()方法創建一個對象,然后由工廠方法提供給調用者。
    • 注意Java中Object提供的clone()方法采用的是“淺”克隆,即只復制關聯對象的引用,而不復制關聯對象的數據。如果需要“深”克隆,則需要在覆蓋clone()方法時手動控制克隆的深度。

原型模式示例解析:郵件群發

>>示例源碼<<<

本案例是一個對象多個修改者情況:

  • 每次發送郵件都是對原始郵件對象克隆然后進行屬性修改
  • 原始郵件對象實現了抽象原型接口
  • 抽象原型接口繼承Cloneable接口,聲明提供抽象原型類型的clone方法聲明
  • 原始郵件類型實現抽象原型類型的clone()方法,用于克隆對象
  • 使用Object的克隆方法來克隆郵件類型對象,淺克隆,復制郵件類對象的引用

類圖

在這里插入圖片描述

1. 抽象原型角色:Prototype.java(接口定義克隆方法)

package com.polaris.designpattern.list1.creational.pattern5.prototype;public interface Prototype extends Cloneable {//克隆方法Prototype clone();
}

2. 具體原型類:Mail.java

創建一個郵件類Mail:

Mail類實現Cloneable接口,并實現了clone()方法,

該方法是實現原型模式的關鍵,只有實現clone()方法,

在應用中才能對Mail進行復制克隆

package com.polaris.designpattern.list1.creational.pattern5.prototype;import lombok.Getter;
import lombok.Setter;public class Mail implements Prototype {//收件人@Getter@Setterprivate String recerver;//郵件標題@Getter@Setterprivate String subject;//稱謂@Getter@Setterprivate String appellation;//郵件內容@Getter@Setterprivate String contxt;//郵件尾部,一般是加上“xxx版權所有”等信息@Getter@Setterprivate String tail;//構造函數public Mail(String subject, String contxt) {this.subject = subject;this.contxt = contxt;}//克隆方法@Overridepublic Prototype clone() {Mail mail = null;try {mail = (Mail) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return mail;}
}

3. 測試類

通過郵件的克隆群發演示原型模式

package com.polaris.designpattern.list1.creational.pattern5.prototype;import java.util.Random;public class DemoTest {//發送賬單的數量,這個值從數據庫中獲得的private static int MAX_COUNT = 6;public static void main(String[] args) {//模擬發送郵件int i = 0;//定義一個郵件對象Prototype prototype = new Mail("某商場五一抽獎活動","五一抽獎活動通知:" +"凡在五一期間在本商場購物滿100元的客戶都有貨的抽獎機會!...");((Mail)prototype).setTail("解釋權歸某商場所有");while (i < MAX_COUNT) {//克隆郵件Mail cloneMail = (Mail) prototype.clone();//以下是每封郵件不同的地方cloneMail.setAppellation(getRandomString(5) + " 先生(女士)");cloneMail.setRecerver(getRandomString(5) + "@" + getRandomString(8) + ".com");//發送郵件sendMail(cloneMail);i++;}}//發送郵件public static void sendMail(Mail mail) {System.out.println("標題:" + mail.getSubject() +"\t收件人:" + mail.getRecerver() + "\t...發送成功!");}//獲取指定長度的隨機字符串public static String getRandomString(int maxLength) {String souce = "abcdefghijklmnopqrstuvwxyz" +"ABCDEFGHIJKLMNOPQRSTUVWXYZ";StringBuilder sb = new StringBuilder();Random random = new Random();for (int i = 0; i < maxLength; i++) {sb.append(souce.charAt(random.nextInt(souce.length())));}return sb.toString();}
}/* Output:
標題:某商場五一抽獎活動	收件人:TEDiM@LXFeyNdU.com	...發送成功!
標題:某商場五一抽獎活動	收件人:qyOWv@wieXPRga.com	...發送成功!
標題:某商場五一抽獎活動	收件人:wMloC@IgFOzjBh.com	...發送成功!
標題:某商場五一抽獎活動	收件人:hrDGv@HAjpARpN.com	...發送成功!
標題:某商場五一抽獎活動	收件人:lyWwt@cLdWntpC.com	...發送成功!
標題:某商場五一抽獎活動	收件人:ZVeap@HZlYxaCe.com	...發送成功!
*///~

補充知識點

Lombok的@Builder注解原理:建造者模式

1. 先說下用法

通過下面的鏈式調用方式,可以非常方便的得到一個需要的user對像

User user = User.builder().name("歷史").age(5000).build();
2. 原理分析

以下是標記了@Builder注解后,編譯出的類文件中看到增加了以下代碼:

public class User extends ConcretePrototype {private String name;private Integer age;User(String name, Integer age) {this.name = name;this.age = age;}public static User.UserBuilder builder() {return new User.UserBuilder();}public static class UserBuilder {private String name;private Integer age;UserBuilder() {}public User.UserBuilder name(String name) {this.name = name;return this;}public User.UserBuilder age(Integer age) {this.age = age;return this;}public User build() {return new User(this.name, this.age);}public String toString() {return "User.UserBuilder(name=" + this.name + ", age=" + this.age + ")";}}
}

通過標記了@Builder注解的類調用類方法builder()方法,這里創建了一個內部類UserBuilder對象

接下來就是構造者模式的實現

內部類UserBuilder就是建造者角色,

User類的內部類UserBuilder的屬性和User類的屬性完全一樣,然后提供了對這些屬性配置的方法,且每個方法都會將該實例返回,這樣就可以進行鏈式調用。

最后提供了一個build()方法,將內部類的屬性賦值給User類的構造函數,來構造User類實例,返回給客戶端。

這就是建造者模式的具體應用。

👈?上一篇:建造者模式 ? ?|?? 下一篇:創建型設計模式對比👉?

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

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

相關文章

必示科技參與智能運維國家標準預研線下編寫會議并做主題分享

近日&#xff0c;《信息技術服務 智能運維 第3部分&#xff1a;算法治理》&#xff08;擬定名&#xff09;國家標準預研階段第一次編寫工作會議在杭州舉行。本次會議由浙商證券承辦。 此次編寫有來自銀行、證券、保險、通信、高校研究機構、互聯網以及技術方等29家單位&#xf…

在云計算環境中,如何實現資源的高效分配和調度?

在云計算環境中&#xff0c;可以通過以下幾種方法實現資源的高效分配和調度&#xff1a; 負載均衡&#xff1a;通過負載均衡算法&#xff0c;將云計算集群的負載均勻地分配到各個節點上。常見的負載均衡算法有輪詢、最小連接數、最短響應時間等。 資源調度算法&#xff1a;為了…

Linux基礎(四):Linux系統文件類型與文件權限

各位看官&#xff0c;好久不見&#xff0c;在正式介紹Linux的基本命令之前&#xff0c;我們首先了解一下&#xff0c;關于文件的知識。 目錄 一、文件類型 二、文件權限 2.1 文件訪問者的分類 2.2 文件權限 2.2.1 文件的基本權限 2.2.2 文件權限值的表示方法 三、修改文…

CSS3 新增背景屬性 + 新增邊框屬性(如果想知道CSS3新增背景屬性和新增邊框屬性的知識點,那么只看這一篇就夠了!)

前言&#xff1a;CSS3在CSS2的基礎上&#xff0c;新增了很多強大的新功能&#xff0c;從而解決一些實際面臨的問題&#xff0c;本篇文章主要講解的為CSS3新增背景屬性和新增邊框屬性。 ???這里是秋刀魚不做夢的BLOG ???想要了解更多內容可以訪問我的主頁秋刀魚不做夢-CSD…

視覺SLAM十四講:從理論到實踐(Chapter5:相機與圖像)

前言 學習筆記&#xff0c;僅供學習&#xff0c;不做商用&#xff0c;如有侵權&#xff0c;聯系我刪除即可 目標 理解針孔相機的模型、內參與徑向畸變參數。理解一個空間點是如何投影到相機成像平面的。掌握OpenCV的圖像存儲與表達方式。學會基本的攝像頭標定方法。 一、相…

機器學習第四十周周報 WDN GGNN

文章目錄 week40 WDN GGNN摘要Abstract一、文獻閱讀1. 題目2. abstract3. 網絡架構3.1 問題提出3.2 GNN3.3 CSI GGNN 4. 文獻解讀4.1 Introduction4.2 創新點4.3 實驗過程4.3.1 數據獲取4.3.2 參數設置4.3.3 實驗結果 5. 結論二、GGNN1. 代碼解釋2. 網絡結構小結參考文獻參考文…

Vue 2 和 Vue 3 中同步和異步

Vue 2 和 Vue 3 中同步和異步 Vue 2 同步和異步 同步更新 (Synchronous Updates) Vue 2 在數據更新后會進行同步渲染更新,但為了性能優化,Vue 會在內部隊列中異步地進行 DOM 更新。這意味著數據變化會立即被捕捉到,但實際的 DOM 更新會被推遲到下一個事件循環隊列中。new V…

基礎3 探索JAVA圖形編程桌面:邏輯圖形組件實現

在一個寬敞明亮的培訓教室里&#xff0c;陽光透過窗戶柔和地灑在地上&#xff0c;教室里擺放著整齊的桌椅。臥龍站在講臺上&#xff0c;面帶微笑&#xff0c;手里拿著激光筆&#xff0c;他的眼神中充滿了熱情和期待。他的聲音清晰而洪亮&#xff0c;傳遍了整個教室&#xff1a;…

Linux模擬考試

注意&#xff0c;以下答案僅供參考 1、某CentOS系統空間不夠&#xff0c;現加一塊100G的硬盤(是系統的第二塊硬盤&#xff09;&#xff0c;分為一個區99G&#xff0c;掛載點是/data&#xff0c;請寫出從分區到掛載并使用的整個步驟及相關命令。 1.創建分區&#xff1a; sudo f…

HTML5 通信方式及應用

目錄 postMessage APIWebSocketsServer-Sent Events (SSE)Fetch API / XMLHttpRequest (XHR)Web Workers & Service WorkersHTML5 提供了多種通信方式,使得瀏覽器中的Web應用能夠實現頁面間、窗口間、甚至與外部服務的有效通信。這些通信方式大大提升了Web應用的交互性和復…

Hsql每日一題 | day02

前言 就一直向前走吧&#xff0c;沿途的花終將綻放~ 題目&#xff1a;主播同時在線人數問題 如下為某直播平臺主播開播及關播時間&#xff0c;根據該數據計算出平臺最高峰同時在線的主播人數。 id stt edt 1001,2021-06-14 12:12:12,2021-06-14 18:1…

【錯誤解決】使用HuggingFaceInstructEmbeddings時的一個錯誤

起因&#xff1a;使用huggingface構建一個問答程序時出現的問題。 錯誤內容&#xff1a; 分析&#xff1a; 查看代碼發現&#xff0c;HuggingFaceInstructEmbeddings和sentence-transformers模塊版本不兼容導致。 可以明顯看到方法參數不同。 解決&#xff1a; 安裝sentenc…

json 讀寫 python

目錄 這里對json保存做了格式封裝 調用代碼&#xff1a; python json原始保存是所有json保存一行&#xff0c; 這里對json保存做了格式封裝 import numpy as np class MyEncoder(json.JSONEncoder):def default(self, obj):if isinstance(obj, np.integer):return int(obj)…

element-ui的Form 表單有些項的參數校驗

項目場景&#xff1a; 提示&#xff1a;項目相關背景&#xff1a; 項目場景&#xff1a;有時候自己的Form 表單中的某幾項引入的一些項不好去校驗 這樣的咋去校驗呢&#xff1f; 解決方案&#xff1a; 提示&#xff1a;問題的具體解決方案&#xff1a; 例如&#xff1a;寫一…

【pyspark速成專家】3_Spark之RDD編程1

目錄 ?編輯 一&#xff0c;創建RDD 二&#xff0c;常用Action操作 三&#xff0c;常用Transformation操作 一&#xff0c;創建RDD 創建RDD主要有兩種方式&#xff0c;一個是textFile加載本地或者集群文件系統中的數據&#xff0c; 第二個是用parallelize方法將Driver中的…

fortran77 初始化矩陣 打印矩陣 模版 備拷

1&#xff0c;源碼 SUBROUTINE INIT_MATRIX(A, m, n, lda)DOUBLE PRECISION A(*)CALL SRAND(2024)DO i1, mDO j1, nA(i lda*(j-1)) RAND() RAND() C WRITE(*, (F8.4)) A(i)END DOEND DOENDSUBROUTINE PRINT_MATRIX(A, m, n, lda)DOUBLE PREC…

解釋Python中的上下文管理器(context manager)

Python中的上下文管理器&#xff08;Context Manager&#xff09;是一種用于管理某些資源的對象&#xff0c;如文件、網絡連接、數據庫連接等。這些資源在使用完畢后需要進行清理操作&#xff0c;如關閉文件、斷開連接等。通過上下文管理器&#xff0c;Python提供了一種優雅的方…

【Vue3】封裝axios請求(cli和vite)

原文作者&#xff1a;我輩李想 版權聲明&#xff1a;文章原創&#xff0c;轉載時請務必加上原文超鏈接、作者信息和本聲明。 Vue 【Vue3】env環境變量的配置和使用&#xff08;區分cli和vite&#xff09; 文章目錄 Vue前言一、常見用法二、vue3cli封裝接口1..env配置2..dev(開…

ADC協議詳解

文章目錄 簡介工作流程原理圖時序圖 優點與缺點 簡介 模數轉換器&#xff08;ADC&#xff0c;Analog-to-Digital Converter&#xff09;是一種將模擬信號轉換為數字信號的電子設備。模擬信號通常表示物理測量的連續變化&#xff0c;如聲音、溫度、壓力等&#xff0c;而數字信號…

codewars check_same_case 題解

題目 編寫一個函數來檢查兩個給定的字符是否大小寫相同。 如果任何字符不是字母&#xff0c;則返回-1如果兩個字符大小寫相同&#xff0c;則返回1如果兩個字符都是字母且大小寫不同&#xff0c;則返回0 例子 a并g返回1A并C返回1b并G返回0B并g返回00并?返回-1題解 1 此題主…