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

来源: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
  • 就像 Web Api 介面可以對入參進行驗證,避免用戶傳入非法的或者不符合我們預期的參數一樣,選項也可以對配置源的內容進行驗證,避免配置中的值與選項類中的屬性不對應或者不滿足預期,畢竟大部分配置都是通過字元串的方式,驗證是很有必要的。 1. 註解驗證 像入參驗證一樣,選項驗證也可以通過特性註解方便地 ...
  • 原文作者:aircraft 原文鏈接:https://www.cnblogs.com/DOMLX/p/17270107.html 加工的泛型類如下: using System; using System.Collections.Generic; using System.IO; using Syst ...
  • 在前一篇文章,我們瞭解瞭如何通過.NET6+Quartz開發基於控制台應用程式的定時任務,今天繼續在之前的基礎上,進一步講解基於ASP.NET Core MVC+Quartz實現定時任務的可視化管理頁面,僅供學習分享使用,如有不足之處,還請指正。 涉及知識點 Quartz組件,關於Quartz組件的 ...
  • 面向對象1 面向對象,更在乎的結果,而過程的實現並不重要 IDea快捷鍵(基礎版) | 快捷鍵 | 作用 | | | | | ctrl + / | 快捷註釋 | | ctrl + shift + / | 多行註釋 | | ctrl + d | 快速複製 | | ctrl + shift + up/d ...
  • NX中的checkmate功能是用於檢查模型、圖紙數據的工具,在UGOPEN中有例子。手動操作可以檢查已載入的裝配下所有零部件,可以設置通過後保存模型,檢查結果保存到Teamcenter中,預設保存在零組件版本下。 代碼中可以設置多個檢查規則。相關設置可以在用戶預設設置中進行設置。 1 // 2 / ...
  • JavaSE 運算符 算術運算符:+,-,*,/,%,++(自增),--(自減) i++:先用後+1;++i:先+1後用 賦值運算符:= 擴展賦值運算符:+=,-=,*=,/= a+=b >a=a+b: ​ 可讀性差,但是編譯效率高,且會自動進行類型轉換; ​ 當ab為基本數據類型時,a+b和b+a ...
  • 面向對象2 訪問修飾符 | | private | default | protected | public | | | | | | | | 當前類 | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | :heavy_che ...
  • 推薦一些學習qml教程 Qt官方的QML教程: https://doc.qt.io/qt-5/qtqml-index.html 這是一個由Qt官方提供的完整的QML教程,包含了所有基本知識和高級語法。 QML中文網:http://www.qmlcn.com/ 這是一個非常不錯的中文QML學習網站,提 ...
  • QAbstractBUtton: 所有按鈕控制項的基類 提供按鈕的通用功能 繼承自QWidget 屬於抽象類別,不能直接去使用,必須藉助於子類(除非你覺得子類不夠用,想自定義一個按鈕) 大部分功能之前已經使用過,在這裡只作簡單介紹 文本設置: setText(str) :設置按鈕提示文本 text() ...
  • 使用 VLD 記憶體泄漏檢測工具輔助開發時整理的學習筆記。本篇介紹 VLD 配置文件中配置項 StartDisabled 的使用方法。 ...