设计模式导读:建造者模式的细腻之处与编程技巧

笔者的碎碎念

其实之前有写过建造者模式的文章,但是感觉其实写的不怎么样,而且自己也理解的一般,但是阅读一些框架源码发现,这些模式真的蛮重要的,很多框架例如OkHttpRetrofit等等都大量使用了建造者模式以及一些其他的设计模式,于是决定对这些模式进行学习和整理,将写一个专栏来记录自己的的学习记录,你知道的,好记性不如烂笔头,加油!

建造者模式的由来

因 Java 中没有命名参数的概念,当一个类的构造器可选参数太多的时候,代码可读性会变得很差。我们通过一个例子来说明,假设我们有一个连接池的配置类 ConnectionPoolConfig,它包含了多个可选参数,比如 maxConnections(最大连接数)、minConnections(最小连接数)、timeout(超时时间)等。为了支持不同的配置选项,最初可能会使用伸缩式构造器模式或者JavaBeans构造器模式来创建这个对象。

伸缩式构造器模式

public class ConnectionPoolConfig {
    private int maxConnections;
    private int minConnections;
    private int timeout;

    public ConnectionPoolConfig(int maxConnections, int minConnections, int timeout) {
        this.maxConnections = maxConnections;
        this.minConnections = minConnections;
        this.timeout = timeout;
    }
...省略ConnectionPoolConfig中的其他构造方法
    // Getters and setters
}

使用伸缩式构造器模式,我们可能会遇到以下问题:

  • 参数顺序依赖性: 如果某些参数是可选的,并且它们的顺序与构造函数中的参数顺序不匹配,那么我们就不得不在构造对象时填充未使用的默认值,比如:

    ConnectionPoolConfig config = new ConnectionPoolConfig(10, 5, 0); // timeout 默认为 0
    
  • 参数类型相似性: 如果两个参数类型相似(比如都是整数),在构造对象时容易搞错参数的顺序,这可能会导致严重的错误。

JavaBeans构造器模式

然后,我们针对这些进行改进,有了后来的JavaBeans构造器模式。

public class ConnectionPoolConfig {
    private int maxConnections;
    private int minConnections;
    private int timeout;

    public ConnectionPoolConfig() {
        // Empty constructor
    }

    // Setters
    public void setMaxConnections(int maxConnections) {
        this.maxConnections = maxConnections;
    }

    public void setMinConnections(int minConnections) {
        this.minConnections = minConnections;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }
...省略
    // Getters
}

使用JavaBeans构造器模式,虽然解决了参数顺序依赖性的问题,但引入了新的问题: 

  • 对象状态不一致性: 构建对象需要多次调用不同的 setter 方法,这可能会导致对象在构造过程中处于不一致的状态。例如,如果某个字段在设置之前被访问,可能得到不完整或不正确的对象状态。 

建造者模式

于是,建造者模式(Builder Pattern)应运而生。

建造者模式在这些问题的基础上提供了更加灵活和安全的对象构建方式:

public class ConnectionPoolConfig {
    private int maxConnections;
    private int minConnections;
    private int timeout;

    private ConnectionPoolConfig(Builder builder) {
        this.maxConnections = builder.maxConnections;
        this.minConnections = builder.minConnections;
        this.timeout = builder.timeout;
    }

    // Getters

    public static class Builder {
        private int maxConnections;
        private int minConnections;
        private int timeout;

        public Builder() {
            // 默认值或者空构造器
        }

        public Builder maxConnections(int maxConnections) {
            this.maxConnections = maxConnections;
            return this;
        }

        public Builder minConnections(int minConnections) {
            this.minConnections = minConnections;
            return this;
        }

        public Builder timeout(int timeout) {
            this.timeout = timeout;
            return this;
        }

        public ConnectionPoolConfig build() {
            return new ConnectionPoolConfig(this);
        }
    }
}

优点: 

  • 链式调用: 使用建造者模式,可以使用链式调用来设置对象的各个属性,清晰地表达出构建对象的步骤和顺序。
ConnectionPoolConfig config = new ConnectionPoolConfig.Builder()
                                  .maxConnections(10)
                                  .minConnections(5)
                                  .timeout(0)
                                  .build();

  • 对象不可变性: 在建造者模式中,可以将对象设计为不可变的(Immutable),一旦构建完成后,对象的状态不可修改,保证了对象的线程安全性和一致性。
  • 消除对象状态不一致性问题: 建造者模式通过在最终构建之前保持对象状态的一致性,避免了JavaBeans模式中可能出现的对象状态不一致性问题。
  • 易于解耦:将产品本身与产品创建过程进行解耦,可以使用相同的创建过程来得到不同的产品。也就说细节依赖抽象。
  • 易于精确控制对象的创建:将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰
  • 易于拓展:增加新的具体建造者无需修改原有类库的代码,易于拓展,符合“开闭原则“。

每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。 

模式原理

使用场景 

建造者模式适用于需要创建复杂对象(对象有多个部分,且构建过程复杂)的场景,或者需要创建多个相似对象(只有部分属性不同)的场景。它有效地解决了伸缩式构造器模式和JavaBeans构造器模式存在的问题,并提供了一种更加优雅和灵活的解决方案。你经常能在Android看到一些常见的技术框架中都使用了该模式,例如OkHttp框架中就有它的大量使用。关于OkHttp我也有文章进行分析,如果你感兴趣,可以去看看OkHttp中是如何使用建造者模式的->【传送门】。

使用 

  1. 构造者的创建:客户端通过创建一个具体的建造者对象(如 Builder),并使用链式调用来设置产品的各个属性
     

    ConnectionPoolConfig config = new ConnectionPoolConfig.Builder()
                                    .maxConnections(10)
                                    .minConnections(5)
                                    .timeout(30)
                                    .build();
    
  2. 属性设置

    每次调用建造者的设置方法(如 maxConnectionsminConnectionstimeout)时,建造者内部会更新自己的状态,以便在构建最终产品时使用。
  3. 构建产品

    最终调用 build() 方法时,建造者将使用其内部状态来实例化并初始化产品对象 ConnectionPoolConfig,并返回给客户端。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/737414.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

将知乎专栏文章转换为 Markdown 文件保存到本地

一、参考内容 参考知乎文章代码 | 将知乎专栏文章转换为 Markdown 文件保存到本地,利用代码为GitHub:https://github.com/chenluda/zhihu-download。 二、步骤 1.首先安装包flask、flask-cors、markdownify 2. 运行app.py 3.在浏览器中打开链接&…

了解请求参数与响应参数的区别:初学者指南

在 Web 的开发领域,无论你是前端开发还是后端开发人员,把握请求与响应参数的核心差异是极其重要的。这些参数在客户端和服务器之间的互动中扮演着关键角色。 请求参数的定义及类别 定义 当客户端向服务器提交信息时所使用的数据被称为请求参数。这些参…

深入理解TCP协议:工作原理、报文结构及应用场景

TCP协议详解 TCP(Transmission Control Protocol,传输控制协议)是因特网协议套件中最重要的协议之一。它为应用程序提供了可靠、面向连接的通信服务。TCP协议确保数据包按顺序到达,并且没有丢失或重复。本文将详细介绍TCP协议的工…

免费ddns工具,快解析DNS解析使用教程

DDNS(Dynamic Domain Name Server),中文叫动态域名解析,主要用于没有固定公网ip的网络环境下,使用一个固定的域名,解析动态变化的ip地址,达到远程访问的目的。 众所周知,目前公网ip资源非常紧缺…

Springboot多模块项目从0构建打包运行

今天复习了一下Springboot的多模块的构建,其实一直以来都对单体项目使用多模块感到不太理解,不知道到底有什么样的优势,目前切身体会到的优势就是确实可以让依赖的划分更加清晰(每个模块下的pom文件只引入该模块需要的依赖&#x…

基于imx6ull开发板 移植opencv4.7.0

一、概述 本章节是针对opencv-4.7.0移植到Linux系统,运行在正点原子-I.MX6U ALPHA开发板 上,详细的移植流程如下。 二、环境要求 2.1 硬件环境 正点原子-I.MX6U ALPHA开发板虚拟机:VMware 2.2 软件环境 Ubuntu系统要求:20.0…

常用算法及参考算法 (1)累加 (2)累乘 (3)素数 (4)最大公约数 (5)最值问题 (6)迭代法

常用算法及参考算法 &#xff08;1&#xff09;累加 &#xff08;2&#xff09;累乘 &#xff08;3&#xff09;素数 &#xff08;4&#xff09;最大公约数 &#xff08;5&#xff09;最值问题 &#xff08;6&#xff09;迭代法 1. 累加 #include <stdio.h>int main() {…

物理层(一)

第2章 物理层 2.1 通信基础 2.1.1 基本概念 1、数据、信号与码元 通信的目的是传输信息&#xff0c;如文字、图像和视频等。数据是指传送信息的实体。信号则是数据的电气或电磁表现&#xff0c;是数据在传输过程中的存在形式。数据和信号都有模拟或数字之分:①模拟数据(或模…

Day15 —— 大语言模型简介

大语言模型简介 大语言模型基本概述什么是大语言模型主要应用领域大语言模型的关键技术大语言模型的应用场景 NLP什么是NLPNLP的主要研究方向word2vecword2vec介绍word2vec的两种模型 全连接神经网络神经网络结构神经网络的激活函数解决神经网络过拟合问题的方法前向传播与反向…

【数据结构与算法】详解循环队列:基于数组实现高效存储与访问

&#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《数据结构与算法》 期待您的关注 ​ 目录 一、引言 &#x1f343;队列的概念 &#x1f343;循环队列的概念 &#x1f343;为什…

python基础语法 003-1 数据类型列表

1 列表 1.1 列表的定义 关键字&#xff1a;list()标识&#xff1a;[] 用逗号隔开使用最频繁的数据类型&#xff0c;列表可以完成大多数集合类型的数据结构实现支持字符、数字、字符串、甚至可以包含列表&#xff0c;即嵌套打印列表类型&#xff1a;type()1个元素的列表表示&…

【Linux基础】文件基本属性

Linux文件基本属性是指文件或目录在Linux系统中具有的一系列特性和信息。这些属性提供了关于文件或目录的详细信息&#xff0c;包括其类型、权限、大小、创建和修改时间等。 文件属性 在 Linux 中我们可以使用 ll 或者 ls –l 命令来显示一个文件的属性以及文件所属的用户和组…

springCloud组件专题(四) --- sentinel

前言 限流&#xff0c;熔断降级概念 限流&#xff1a;顾名思义&#xff0c;就是对一个资源&#xff08;服务或者接口都可以算资源&#xff09;的访问进行限制。简单来说就是限制单位时间内允许资源被访问的次数。常见的算法就是令牌桶算法。 降级&#xff1a;降级其实是一种资源…

常用主流sip协议软电话客户端软件有哪些?—— 筑梦之路

Ekiga 官网地址&#xff1a;Ekiga ~ Free Your Speech Ekiga&#xff0c;原名GnomeMeeting&#xff0c;支持Windows和Linux&#xff0c;是一个兼容SIP和H.323的视频会议程序&#xff0c;兼容VoIP&#xff0c;IP电话&#xff0c;通过Ekiga可以与使用任何SIP和H.323软硬件的远程…

C语言 | Leetcode C语言题解之第188题买卖股票的最佳时机IV

题目&#xff1a; 题解&#xff1a; int maxProfit(int k, int* prices, int pricesSize) {int n pricesSize;if (n 0) {return 0;}k fmin(k, n / 2);int buy[k 1], sell[k 1];memset(buy, 0, sizeof(buy));memset(sell, 0, sizeof(sell));buy[0] -prices[0];sell[0] 0…

周末设计高端企业_集团官网主题Discuz模板

风格名称: 周末设计_高端企业_集团官网 适用版本: Discuz! X3.0、X3.1、X3.2、X3.3、F1.0 风格编码: 使用语言包结构&#xff0c;适合全部编码 周末设计高端企业_集团官网主题Discuz模板

g++制作C++动态库的简洁例程

g制作C动态库的简洁例程 code review! 文章目录 g\制作C动态库的简洁例程1. 创建 C 动态库1.1 动态库源文件1.2 编译动态库 2. 使用动态库2.1 命令行编译链接然后运行2.2 使用 CMake 编译链接然后运行 3.附加笔记&#xff1a;关于运行时是否能找到libmylib.so的问题汇总3.1.g -…

TLS握手中的RTT

文章目录 TLS 1.2 握手过程中的 RTT 次数TLS 1.3 1-RTT 初次TLS1.3 0-RTT 握手过程总结 TLS 1.2 握手过程中的 RTT 次数 TLS 1.2 握手通常需要2 RTT 才能完成。具体步骤如下&#xff1a; 第一次 RTT&#xff1a; 客户端发送 ClientHello&#xff1a;客户端生成一个随机数&…

【离散数学】图的随机生成和欧拉(回)路的确定(c语言实现)

实验要求 变量定义 因为如果我们使用局部变量&#xff0c;每一个函数都会使用这些变量&#xff0c;会让函数的参数越变越多。所以我们定义全局变量&#xff0c;这样就不用在参数中调用了。 #define MAX 100 int arrMap[MAX][MAX] { 0 };//图的矩阵 int degree[MAX] { 0 };…

GEO数据挖掘-富集分析、TinyArray简化流程、多组样本分析more

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 富集分析一些理论知识具体代码 富集不到的补救措施更多资料---问题数据和常见错误分析Part4-复杂数据及其分析多分组数据分析流程 tinyarray简化版本分析流程多分组…