設計模式---原型模式

来源: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
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...