設計模式---原型模式

来源:https://www.cnblogs.com/buzuweiqi/archive/2022/09/22/16709147.html
-Advertisement-
Play Games

簡述 類型:創建型 目標:通過拷貝快速創建相同或相似對象。 接下來我們看一個需要改進的案例。 優化案例 話不多說,先來看一個創建相同或相似對象的傳統寫法。 原版v0 public class Department { private String name; private String count ...


簡述

  • 類型:創建型
  • 目的:通過拷貝快速創建相同或相似對象。

接下來我們看一個需要改進的案例。

優化案例

話不多說,先來看一個創建相同或相似對象的傳統寫法。

最初版v0

public class Department {
    private String name;
    private String country;
    private String province;
    private String city;
    private List<Employee> employees;
    public String getName() {
        return name;
    }
    public String getCountry() {
        return country;
    }
    public String getProvince() {
        return province;
    }
    public String getCity() {
        return city;
    }
    public List<Employee> getEmployees() {
        return employees;
    }
    public Department(String name, String country, String province,
                      String city, List<Employee> employees) {
        this.name = name;
        this.country = country;
        this.province = province;
        this.city = city;
        this.employees = employees;
    }
}
class Employee {
    private String name;
    private String sex;
    private int age;
    private String country;
    private String province;
    private String city;
    private String post;
    public String getName() {
        return name;
    }
    public String getSex() {
        return sex;
    }
    public int getAge() {
        return age;
    }
    public String getCountry() {
        return country;
    }
    public String getProvince() {
        return province;
    }
    public String getCity() {
        return city;
    }
    public String getPost() {
        return post;
    }
    public Employee(String name, String sex, int age,
                    String country, String province,
                    String city, String post) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.country = country;
        this.province = province;
        this.city = city;
        this.post = post;
    }
}

已知一個Department類型的對象,我們想構造一個相似的對象。

public static void main(String[] args) {
    Employee emp = new Employee("張三", "男", 15, "中國", "江西省", "南昌市", "124-1241-1353");
    Department department = new Department("開發部", "中國", "江西省", "南昌市", List.of(e)); // 已知對象
    Department department1 = new Department(department.getName(), department.getCountry(), department.getProvince(), department.getCity(), department.getPost()); // 拷貝對象
}

可以感受到,對象拷貝的朴素寫法非常的麻煩。而且想到每一處對象拷貝都需要這樣寫就感覺頭皮發麻。

為瞭解決這個問題,我們引入原型模式。請看以下樣例。

修改版v1(淺拷貝)

public class Department {
    private String name;
    private String country;
    private String province;
    private String city;
    private List<Employee> employees;
    public Department(String name, String country, String province,
                      String city, List<Employee> employees) {
        this.name = name;
        this.country = country;
        this.province = province;
        this.city = city;
        this.employees = employees;
    }
}
class Employee {
    private String name;
    private String sex;
    private int age;
    private String country;
    private String province;
    private String city;
    private String post;
    public Employee(String name, String sex, int age,
                    String country, String province,
                    String city, String post) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.country = country;
        this.province = province;
        this.city = city;
        this.post = post;
    }
}

使用clone()方法拷貝目標對象。

public static void main(String[] args) throws CloneNotSupportedException {
    Employee e = new Employee("張三", "男", 15, "中國", "江西省", "南昌市", "124-1241-1353");
    Department department = new Department("開發部", "中國", "江西省", "南昌市", List.of(e));
    Department department1 = (Department)department.clone();
    System.out.println(department == department1); // false
    System.out.println(department.employees == department1.employees); // true
}

我們發現第8行輸出true,這說明兩個對象的employees的引用相同,這會導致修改其中一個employees的元素會影響到另一個,這並不好。

如何解決屬性相同引用的問題?看以下樣例。

修改版v2(深拷貝)

public class Department implements Cloneable {
    private String name;
    private String country;
    private String province;
    private String city;
    private List<Employee> employees;
    public Department(String name, String country, String province,
                      String city, List<Employee> employees) {
        this.name = name;
        this.country = country;
        this.province = province;
        this.city = city;
        this.employees = employees;
    }
    @Override
    public Object clone() throws CloneNotSupportedException {
        Department department = (Department)super.clone();
        List<Employee> emps = new ArrayList<>();
        for (int i = 0; i < department.employees.size(); i ++) {
            emps.add((Employee) employees.get(i).clone());
        }
        department.employees = emps;
        return department;
    }
}
class Employee implements Cloneable {
    private String name;
    private String sex;
    private int age;
    private String country;
    private String province;
    private String city;
    private String post;
    public Employee(String name, String sex, int age,
                    String country, String province,
                    String city, String post) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.country = country;
        this.province = province;
        this.city = city;
        this.post = post;
    }
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

使用clone() 拷貝對象,因為類以及類中的屬性也重寫了clone()

public static void main(String[] args) throws CloneNotSupportedException {
    Employee e = new Employee("張三", "男", 15, "中國", "江西省", "南昌市", "124-1241-1353");
    Department department = new Department("開發部", "中國", "江西省", "南昌市", List.of(e));
    Department department1 = (Department)department.clone();
    System.out.println(department == department1); // false
    System.out.println(department.employees == department1.employees); // false
}

雖然這種方式可以深拷貝,但是這會讓代碼量激增。

序列化與反序列化可以解決這個問題。

修改版v3(序列化與反序列化)(推薦使用)

public class Department {
    private String name;
    private String country;
    private String province;
    private String city;
    private List<Employee> employees;
    public Department(String name, String country, String province,
                      String city, List<Employee> employees) {
        this.name = name;
        this.country = country;
        this.province = province;
        this.city = city;
        this.employees = employees;
    }
}
class Employee {
    private String name;
    private String sex;
    private int age;
    private String country;
    private String province;
    private String city;
    private String post;
    public Employee(String name, String sex, int age,
                    String country, String province,
                    String city, String post) {
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.country = country;
        this.province = province;
        this.city = city;
        this.post = post;
    }
}

序列化與反序列化的實現方式有很多種,本文使用Gson來實現。以下是樣例。

public static void main(String[] args) throws CloneNotSupportedException {
    Employee e = new Employee("張三", "男", 15, "中國", "江西省", "南昌市", "124-1241-1353");
    Department department = new Department("開發部", "中國", "江西省", "南昌市", List.of(e));
    Gson gson = new Gson();
    String s = gson.toJson(department);
    Department department1 = s.fromJson(s, Department.class);
    System.out.println(department == department1); // false
    System.out.println(department.employees == department1.employees); // false
}

基於序列化和反序列化實現的克隆不僅僅是深度克隆,更重要的是通過泛型限定,可以檢查出要克隆的對象是否支持序列化,這項檢查是編譯器完成的,不是在運行時拋出異常,這種是方案明顯優於使用Object類的clone方法克隆對象。讓問題在編譯的時候暴露出來總是優於把問題留到運行時。

總結

優點

  1. 由於是直接從記憶體中讀取對象進行克隆,所以性能卓越。
  2. 代碼量不論是相較於傳統寫法要精簡很多,尤其是序列化與反序列化的方式。

缺點

  1. 代碼的理解難度增加。尤其是深拷貝的理解較為複雜。

適用場景

  1. 適用於只有細微參數變動的對象創建。
  2. 適用於需要備份的場景。如,當業務執行過程中,某種情況下需要數據回滾的時候,提前備份可以使用。

本文來自博客園,作者:buzuweiqi,轉載請註明原文鏈接:https://www.cnblogs.com/buzuweiqi/p/16709147.html


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

-Advertisement-
Play Games
更多相關文章
  • 1 微信小程式原生推拉流組件功能簡介 本文將介紹如何使用微信小程式原生推拉流組件 <live-pusher> 和 <live-player> 進行推拉流,快速實現一個簡單的實時音視頻通話。 由於微信小程式原生推拉流組件使用起來比較複雜,推薦開發者使用即構封裝的音視頻SDK <zego-push> 和 ...
  • 1.HTTP和HTTPS的基本概念 http:是一個客戶端和服務端請求和應答的標準(TCP),用於從www伺服器傳輸超文本到本地瀏覽器的超文本傳輸協議。 https:是以安全為目標的HTTP通道,即HTTP下加入SSL層進行加密。其作用是:建立一個信息安全通道,確保數據的傳輸,確保網站的真實性。 補 ...
  • 原因 項目每次打包後都需要改動項目版本號,這個改動每次都需要在package.json中修改version,比較麻煩,到底有沒有一種打包後版本號自加的辦法。 方案 版本號自加其實可以使用fs修改文件來實現的。 具體思路是:在執行打包命令npm run build時,同時執行一段js代碼,該代碼通過調 ...
  • 在我們基於UniApp的H5項目中,需要生成一些二維碼進行展示,另外也需要讓用戶可以掃碼進行一定的快捷操作,本篇隨筆介紹一下二維碼的生成處理和基於H5的掃碼進行操作。二維碼的生成,使用了JS文件weapp-qrcode.js進行處理,而二維碼掃碼則是基於一個第三方組件的方式進行支持的,最後通過統一入... ...
  • 一.起步 1.1 配置uni-app開發環境 什麼是uni-app,就是基於vue的一個開發框架,可以將我們寫的一套代碼,同時發佈到ios、安卓、小程式等多個平臺 ==官方推薦使用Hbuilderx來寫uni-app項目== 下載之後可以將預設改為vscode 進入hbuilder插件市場下載scs ...
  • 蒼穹之邊,浩瀚之摯,眰恦之美; 悟心悟性,善始善終,惟善惟道! —— 朝槿《朝槿兮年說》 寫在開頭 在併發編程領域,有兩大核心問題:一個是互斥,即同一時刻只允許一個線程訪問共用資源;另一個是同步,即線程之間如何通信、協作。主要原因是,對於多線程實現實現併發,一直以來,多線程都存在2個問題: 線程之間 ...
  • 蒼穹之邊,浩瀚之摯,眰恦之美; 悟心悟性,善始善終,惟善惟道! —— 朝槿《朝槿兮年說》 寫在開頭 在併發編程領域,有兩大核心問題:一個是互斥,即同一時刻只允許一個線程訪問共用資源;另一個是同步,即線程之間如何通信、協作。主要原因是,對於多線程實現實現併發,一直以來,多線程都存在2個問題: 線程之間 ...
  • 一、前言 Caffeine是一個高性能的 Java 緩存庫,底層數據存儲採用ConcurrentHashMap 優點:因為Caffeine面向JDK8,在jdk8中ConcurrentHashMap增加了紅黑樹,在hash衝突嚴重時也能有良好的讀性能。多線程環境中,不同的key可以併發寫,相同的ke ...
一周排行
    -Advertisement-
    Play Games
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...