設計模式---建造者模式

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

概要 設計模式類型:創建型 目標問題:創建對象時,參數設置的靈活性問題。(具體看案例) 接下來我們看一個需要改進的案例。 對象創建的優化 現在有個Employee類,你能預想到在開發中可能會出現的問題嗎?不一定是業務方面的問題哦。 最初版 public class Employee { privat ...


概要

  • 設計模式類型:創建型
  • 目標問題:創建對象時,參數設置的靈活性問題。(具體看案例)

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

對象創建的優化

現在有個Employee類,你能預想到在開發中可能會出現的問題嗎?不一定是業務方面的問題哦。

最初版

public class Employee {
    private String name;
    private String sex;
    private int age;
    private String address; // 住址
    private String post; // 郵編
    private String company; // 公司
    private String department; // 部門
    public Employee(String name, String sex, int age,
                    String address, String post, String company,
                    String department) {
        this.name = name;
        this.sex = sex;
        if (!("男".equals(sex) || "女".equals(sex))) {
            throw new RuntimeException("輸入錯誤的性別:" + sex);
        }
        this.age = age;
        if (age <= 1 || age >= 150) {
            throw new RuntimeException("輸入錯誤的年齡:" + age);
        }
        this.address = address;
        this.post = post;
        if (!postCheck()) {
            throw new RuntimeException("地址(" + address + ")與郵編(" + post + ")不一致");
        }
        this.company = company;
        this.department = department;
    }
    private boolean postCheck() {
        if (address == null/* || ... */) { // 非空check,以及其它的check(省略),address的post與設置的post是否一致等
            return false;
        }
        return true;
    }
}

實際上在業務上並沒有過多的問題,最重要的問題就是這個類的使用非常的麻煩。
首先,構造函數只有一個,如果不增加新的構造函數的話無法靈活的傳入不同數量的參數。並且為了讓其參數的設置變得靈活,我們必須重載非常多種不一樣的構造函數,工程量巨大,且枯燥乏味。

為此,我們有了一個新的改進方案。

修改版v1

public class Employee {
    private String name;
    private String sex;
    private int age;
    private String address; // 住址
    private String post; // 郵編
    private String company; // 公司
    private String department; // 部門
    public Employee() {}

    public void setName(String name) {
        this.name = name;
    }

    public void setSex(String sex) {
        this.sex = sex;
        if (!("男".equals(sex) || "女".equals(sex))) {
            throw new RuntimeException("輸入錯誤的性別:" + sex);
        }
    }

    public void setAge(int age) {
        this.age = age;
        if (age <= 1 || age >= 150) {
            throw new RuntimeException("輸入錯誤的年齡:" + age);
        }
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public void setPost(String post) {
        this.post = post;
        if (!postCheck()) {
            throw new RuntimeException("地址(" + address + ")與郵編(" + post + ")不一致");
        }
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    private boolean postCheck() {
        if (address == null/* || ... */) { // 非空check,以及其它的check(省略),address的post與設置的post是否一致等
            return false;
        }
        return true;
    }
}

改成了上述方式後確實使用起來方便不少,不想傳入的參數不調用對應的set方法就好了,並且也省去了大量構造方法的定義。但是,依舊有一個問題,我們來看看使用樣例。

public static void main(String[] args) {
    Employee e = new Employee();
    e.setPost("121-1245-1231"); // 地址(null)與郵編(121-1245-1231)不一致,報錯
}

由於設置post的時候對於address屬性做了非空判斷,所以代碼書寫時address的設置一定要在post之前,否則就會報錯。這無疑增加了項目開發的難度。

既然構造函數麻煩,set也存在一些問題。那我們如何優化Employee對象的創建呢?看下麵的樣例。

修改版v2

public class Employee {
    private String name;
    private String sex;
    private int age;
    private String address; // 住址
    private String post; // 郵編
    private String company; // 公司
    private String department; // 部門
    private Employee(Builder builder) {
        this.name = builder.name;
        this.sex = builder.sex;
        this.age = builder.age;
        this.address = builder.address;
        this.post = builder.post;
        this.company = builder.company;
        this.department = builder.department;
    };
    public static Builder Builder() {
        return new Builder();
    }
    public static class Builder {
        private String name;
        private String sex;
        private int age;
        private String address; // 住址
        private String post; // 郵編
        private String company; // 公司
        private String department; // 部門

        public Builder name(String name) {
            this.name = name;
            return this;
        }
        public Builder sex(String sex) {
            this.sex = sex;
            return this;
        }
        public Builder age(int age) {
            this.age = age;
            return this;
        }
        public Builder address(String address) {
            this.address = address;
            return this;
        }
        public Builder post(String post) {
            this.post = post;
            return this;
        }
        public Builder company(String company) {
            this.company = company;
            return this;
        }
        public Builder department(String department) {
            this.department = department;
            return this;
        }
        public Employee build() {
            if (!("男".equals(sex) || "女".equals(sex))) {
                throw new RuntimeException("輸入錯誤的性別:" + sex);
            }
            if (age <= 1 || age >= 150) {
                throw new RuntimeException("輸入錯誤的年齡:" + age);
            }
            if (!postCheck()) {
                throw new RuntimeException("地址(" + address + ")與郵編(" + post + ")不一致");
            }
            return new Employee(this);
        }
        private boolean postCheck() {
            if (address == null/* || ... */) { // 非空check,以及其它的check(省略),address的post與設置的post是否一致等
                return false;
            }
            return true;
        }
    }
}

使用建造者模式優化對象的創建。客戶端中對象的創建不再使用new關鍵字,不需要定義數量繁多的構造函數以應對複雜多變的屬性設置,並且也有著set的靈活性又不存在屬性設置順序的依賴。以下是使用樣例。

public static void main(String[] args) {
    Employee e = Employee.Builder().name("張三").sex("男").age(35)
        .post("124-1241-1352").address("江西省南昌市").build(); // 執行成功
}

流式編程的風格使得代碼清晰舒爽。

總結

優點

  1. 無需定義大量的構造方法。
  2. 既有set方法的靈活性,又消除了set可能出現的屬性固定順序設置的問題。
  3. 流式編程的風格,代碼清晰簡潔。

缺點

  1. 由於定義了靜態內部類Builder,可能會使得系統的類數量激增,影響性能。
  2. 代碼的理解難度增加。

適用場景

  1. 適用於屬性很多的類的創建。
  2. 適用於對屬性設置判斷條件複雜的類的創建。尤其是屬性設置對於其他屬性有依賴的情況。

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


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

-Advertisement-
Play Games
更多相關文章
  • 隨著前端的範疇逐漸擴大,深度逐漸下沉,富前端必然帶來的一個問題就是性能。特別是在大型複雜項目中,重前端業務可能因為一個小小的數據依賴,導致整個頁面卡頓甚至崩潰。本文基於Quick BI(數據可視化分析平臺)歷年架構變遷中性能的排查、解決和總結出的“個性”問題,嘗試總結整個前端層面相對“共性”的問題,... ...
  • 最近,有群友問我,他們的一個作業,儘量使用少的標簽去實現這樣一個象棋佈局: 他用了 60 多個標簽,而他的同學,只用了 6 個,問我有沒有辦法儘可能的做到利用更少的標簽去完成這個佈局效果。 其實,對於一個頁面的佈局而言,標簽越少不一定是好事,我們在考慮 DOM 的消耗的同時,也需要關註代碼的可讀性, ...
  • ⚠️1.1萬長文⚠️ React源碼並非洪水猛獸,知道方法,就可以很輕易地馴服它(=^▽^=)。文章基於最新的React源碼進行調試及閱讀,將以通俗地方式解讀React ...
  • 前言 如果你第一次接觸秒殺,可能還不太理解,庫存100件就賣100件,在資料庫里減到0就好了,這有什麼麻煩的?理論上是這樣,但是具體到業務場景中就沒那麼簡單了。今天就聊聊減庫存的設計,之後以高可用方案來結束秒殺設計的全部內容。 一、秒殺中的減庫存 減庫存操作一般有如下幾個方式: 1.下單減庫存:下單 ...
  • 我的設計模式之旅。本節學習模板方法模式。從多個類包含許多相似代碼的問題中思考如何在保持演算法結構完整的情況下去除重覆代碼。介紹了模板方法模式的概念和實現方法。 ...
  • 在模板模式(Template Pattern)中,一個抽象類公開定義了執行它的方法的方式/模板。它的子類可以按需要重寫方法實現,但調用將以抽象類中定義的方式進行。這種類型的設計模式屬於行為型模式。 ...
  • 本文的重點在於說明工作中所使用的設計模式,為了能夠更好的理解設計模式,首先簡單介紹一下業務場景。使用設計模式,可以簡化代碼、提高擴展性、可維護性和復用性。有哪些設計模式,這裡就不再介紹了,網上很多,本文只介紹所用到設計模式。 ...
  • 大部分高級編程語言雖然語法不同,編譯器不同,學習它們的小哥哥小姐姐們不同,但有一點卻是出奇地一致:編程邏輯! 有些剛入行或剛入門的童鞋可能連編程是啥意思都沒弄懂,一下子又來了個「邏輯」,那是什麼?這裡說的邏輯,廣義上指的是抽象思維能力,也就是能思考那些客觀世界不存在的東西的能力。狹義上來說,就是明確 ...
一周排行
    -Advertisement-
    Play Games
  • 1. 說明 /* Performs operations on System.String instances that contain file or directory path information. These operations are performed in a cross-pla ...
  • 視頻地址:【WebApi+Vue3從0到1搭建《許可權管理系統》系列視頻:搭建JWT系統鑒權-嗶哩嗶哩】 https://b23.tv/R6cOcDO qq群:801913255 一、在appsettings.json中設置鑒權屬性 /*jwt鑒權*/ "JwtSetting": { "Issuer" ...
  • 引言 集成測試可在包含應用支持基礎結構(如資料庫、文件系統和網路)的級別上確保應用組件功能正常。 ASP.NET Core 通過將單元測試框架與測試 Web 主機和記憶體中測試伺服器結合使用來支持集成測試。 簡介 集成測試與單元測試相比,能夠在更廣泛的級別上評估應用的組件,確認多個組件一起工作以生成預 ...
  • 在.NET Emit編程中,我們探討了運算操作指令的重要性和應用。這些指令包括各種數學運算、位操作和比較操作,能夠在動態生成的代碼中實現對數據的處理和操作。通過這些指令,開發人員可以靈活地進行算術運算、邏輯運算和比較操作,從而實現各種複雜的演算法和邏輯......本篇之後,將進入第七部分:實戰項目 ...
  • 前言 多表頭表格是一個常見的業務需求,然而WPF中卻沒有預設實現這個功能,得益於WPF強大的控制項模板設計,我們可以通過修改控制項模板的方式自己實現它。 一、需求分析 下圖為一個典型的統計表格,統計1-12月的數據。 此時我們有一個需求,需要將月份按季度劃分,以便能夠直觀地看到季度統計數據,以下為該需求 ...
  • 如何將 ASP.NET Core MVC 項目的視圖分離到另一個項目 在當下這個年代 SPA 已是主流,人們早已忘記了 MVC 以及 Razor 的故事。但是在某些場景下 SSR 還是有意想不到效果。比如某些靜態頁面,比如追求首屏載入速度的時候。最近在項目中回歸傳統效果還是不錯。 有的時候我們希望將 ...
  • System.AggregateException: 發生一個或多個錯誤。 > Microsoft.WebTools.Shared.Exceptions.WebToolsException: 生成失敗。檢查輸出視窗瞭解更多詳細信息。 內部異常堆棧跟蹤的結尾 > (內部異常 #0) Microsoft ...
  • 引言 在上一章節我們實戰了在Asp.Net Core中的項目實戰,這一章節講解一下如何測試Asp.Net Core的中間件。 TestServer 還記得我們在集成測試中提供的TestServer嗎? TestServer 是由 Microsoft.AspNetCore.TestHost 包提供的。 ...
  • 在發現結果為真的WHEN子句時,CASE表達式的真假值判斷會終止,剩餘的WHEN子句會被忽略: CASE WHEN col_1 IN ('a', 'b') THEN '第一' WHEN col_1 IN ('a') THEN '第二' ELSE '其他' END 註意: 統一各分支返回的數據類型. ...
  • 在C#編程世界中,語法的精妙之處往往體現在那些看似微小卻極具影響力的符號與結構之中。其中,“_ =” 這一組合突然出現還真不知道什麼意思。本文將深入剖析“_ =” 的含義、工作原理及其在實際編程中的廣泛應用,揭示其作為C#語法奇兵的重要角色。 一、下劃線 _:神秘的棄元符號 下劃線 _ 在C#中並非 ...