Java设计模式:建造者模式之经典与流式的三种实现(四)

news2025/6/20 8:05:50

本文将深入探讨Java中建造者模式的两种实现方式:经典建造者与流式建造者。建造者模式是一种创建型设计模式,它允许你构建复杂对象的步骤分解,使得对象的创建过程更加清晰和灵活。我们将通过示例代码详细解释这两种实现方式,并分析它们各自的特点和使用场景。

[参见]:

Java设计模式:核心概述(一)

Java设计模式:单例模式之六种实现方式详解(二)

Java设计模式:工厂模式之简单工厂、工厂方法、抽象工厂(三)

目录

    • 一、前言
    • 二、建造者模式的使用场景
    • 三、建造者模式的三种实现方式
      • 3.1 经典建造者模式
      • 3.2 流式建造者模式
      • 3.3 使用Lombok库实现流式建造者模式
    • 四、建造者模式的注意事项
    • 五、结语

一、前言

开发中,我们经常需要创建具有多个属性或配置选项的复杂对象。直接通过构造函数或大量的setter方法来设置这些属性可能会导致代码的可读性和维护性下降。建造者模式通过提供一个建造者类来封装复杂对象的创建过程,使得客户端代码可以更加简洁和清晰地构建对象。

二、建造者模式的使用场景

  1. 当对象有多个构造函数参数时:如果一个类的构造函数需要多个参数,而这些参数中有些是可选的,那么使用建造者模式可以更加清晰地表达对象的创建过程,避免构造函数的参数列表过长和混乱。

  2. 当对象的构建过程需要多个步骤时:如果对象的创建过程涉及多个步骤,并且这些步骤的顺序很重要,那么建造者模式可以确保这些步骤按照正确的顺序执行,并提供一种灵活的方式来调整这些步骤。

  3. 当需要创建不同配置的对象变体时:如果需要根据不同的配置选项创建对象的多个变体,那么建造者模式可以提供一种清晰的方式来表示这些配置选项,并根据需要构建不同的对象变体。

  4. 当对象的构建过程需要复杂的逻辑时:如果对象的构建过程涉及复杂的逻辑,如验证参数的有效性、计算属性的默认值等,那么建造者模式可以将这些逻辑封装在建造者类中,使客户端代码更加简洁和清晰。

在这里插入图片描述


三、建造者模式的三种实现方式

Java中,实现建造者设计模式通常有两种主要方式:经典的建造者模式(也称为分步构建器)和流式建造者模式(也称为流畅接口构建器或链式调用构建器)。第三种借用lomback自动生成流式建造者模式,所以实际是两种方式。

3.1 经典建造者模式

经典建造者模式通常包括一个产品类、一个抽象建造者接口、一个具体建造者类和一个指挥者类(可选)。产品类包含需要设置的属性和访问器方法。抽象建造者接口定义了构建产品所需的各个步骤。具体建造者类实现了抽象建造者接口,并提供了设置产品属性和获取构建好的产品对象的方法。

然而,在实际应用中,我们往往可以省略指挥者类,直接在客户端代码中操作具体建造者来构建产品对象。这种方式简化了经典建造者模式的结构,但仍然保留了其逐步构建对象的优点。

代码:

// 产品类  
public class Product {  
    private String partA;  
    private String partB;  
    private String partC;  
  
    // 构造方法可以是私有的,因为建造者模式可以控制对象的构建过程  
    private Product() {}  
  
    public String getPartA() {  
        return partA;  
    }  
  
    public void setPartA(String partA) {  
        this.partA = partA;  
    }  
  
    // 省略其他getter和setter方法...  
  
    // 静态内部类作为具体建造者  
    public static class Builder {  
        private Product product;  
  
        public Builder() {  
            product = new Product();  
        }  
  
        public Builder setPartA(String partA) {  
            product.partA = partA;  
            return this; // 返回当前Builder实例,以便链式调用  
        }  
  
        public Builder setPartB(String partB) {  
            product.partB = partB;  
            return this;  
        }  
  
        public Builder setPartC(String partC) {  
            product.partC = partC;  
            return this;  
        }  
  
        // 创建并返回产品对象  
        public Product build() {  
            return product;  
        }  
    }  
}  
  
// 客户端代码  
public class Client {  
    public static void main(String[] args) {  
        Product product = new Product.Builder()  
            .setPartA("This is part A")  
            .setPartB("This is part B")  
            .setPartC("This is part C")  
            .build();  
  
        System.out.println(product.getPartA());  
        // 省略访问其他部分...  
    }  
}

Product 类有一个静态内部类 Builder,它负责构建 Product 对象。客户端代码通过调用 Builder 的方法来设置产品的各个部分,并最终调用 build() 方法来获取构建好的产品对象。

3.2 流式建造者模式

流式建造者模式是经典建造者模式的一种变体,它更加强调链式调用的流畅性。在流式建造者模式中,产品类通常包含一个静态内部类作为流式建造者。这个内部类提供了设置产品属性的方法,并返回自身的实例以支持链式调用。最后,通过一个build()方法返回构建好的产品对象。

流式建造者模式省略了经典建造者模式中的抽象建造者接口和指挥者类,使得代码更加简洁和直观。同时,通过链式调用的方式设置产品属性,可以提高代码的可读性和编写的灵活性。

代码:

// 产品类  
public class Product {  
    private String partA;  
    private String partB;  
    private String partC;  
  
    // 私有构造方法,防止直接实例化  
    private Product() {}  
  
    // 省略getter方法...  
  
    // 静态内部类作为流式建造者  
    public static class Builder {  
        private String partA;  
        private String partB;  
        private String partC;  
  
        public Builder withPartA(String partA) {  
            this.partA = partA;  
            return this;  
        }  
  
        public Builder withPartB(String partB) {  
            this.partB = partB;  
            return this;  
        }  
  
        public Builder withPartC(String partC) {  
            this.partC = partC;  
            return this;  
        }  
  
        // 创建产品对象  
        public Product build() {  
            Product product = new Product();  
            product.partA = this.partA;  
            product.partB = this.partB;  
            product.partC = this.partC;  
            return product;  
        }  
    }  
}  
  
// 客户端代码  
public class Client {  
    public static void main(String[] args) {  
        Product product = new Product.Builder()  
            .withPartA("This is part A")  
            .withPartB("This is part B")  
            .withPartC("This is part C")  
            .build();  
  
        // 省略访问产品属性的代码...  
    }  
}

Builder 类的每个设置方法都返回 Builder 类型的实例(即 this),从而允许链式调用。这种方式使得客户端代码更加简洁和易读。注意,在这个实现中,Product 类的构造方法是私有的,以防止外部直接实例化。所有的属性设置都是通过 Builder 进行的。

3.3 使用Lombok库实现流式建造者模式

Lombok 是一个 Java 库,它可以通过注解来简化 Java 代码,例如自动生成 getter、setter、equals、hashCode 和 toString 方法等。其中,@Builder 注解就是用来实现建造者模式的一个简化工具。

使用 Lombok 的 @Builder 注解,你不需要手动编写建造者模式的代码。只需要在类定义上添加 @Builder 注解,Lombok 就会在编译时自动生成相应的建造者类。

下面是一个使用 Lombok @Builder 注解来实现建造者模式的代码:

import lombok.Builder;
import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@Builder
public class Person {
    private String name;
    private int age;
    private String address;

    // 使用 Lombok @Builder 注解后,无需再手动编写建造者模式的代码
    // Lombok 会在编译时自动生成一个名为 Person.PersonBuilder 的内部类

    // 下面是如何使用生成的建造者类来构建 Person 对象的示例
    public static void main(String[] args) {
        Person person = Person.builder()
                .name("John Doe")
                .age(30)
                .address("123 Main St")
                .build();
        
        System.out.println(person);
    }
}

在这个例子中,我们定义了一个 Person 类,并使用了 @Getter@Setter@Builder 注解。@Getter@Setter 注解分别用于生成 getter 和 setter 方法,而 @Builder 注解则用于生成建造者模式的代码。

main 方法中,我们使用 Person.builder() 方法来获取一个 PersonBuilder 实例,然后通过链式调用设置 nameageaddress 属性,最后调用 build() 方法来构建 Person 对象。

以上使用 Lombok 的 @Builder 注解所生成的代码在编译时会创建一个内部类,这个内部类通常命名为 类名Builder,在这个例子中是 PersonBuilder。这个内部类会包含对应类中所有字段的设置方法以及一个 build() 方法来创建目标对象。

生成的代码大致相当于手动实现的流式建造者模式,因为它允许通过链式调用的方式来设置对象的属性。不过,由于 Lombok 自动处理了这些细节,用户无需手动编写这些代码。

假设没有 Lombok,并且我们要手动实现上述 Person 类的建造者模式,代码可能看起来像这样:

public class Person {
    private String name;
    private int age;
    private String address;

    private Person(Builder builder) {
        this.name = builder.name;
        this.age = builder.age;
        this.address = builder.address;
    }

    public static class Builder {
        private String name;
        private int age;
        private String address;

        public Builder() {}

        public Builder name(String name) {
            this.name = name;
            return this;
        }

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

        public Builder address(String address) {
            this.address = address;
            return this;
        }

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

    // getters, setters, toString, etc.
}

使用 Lombok,上面的所有建造者逻辑都会在编译时自动生成,你不需要手动编写这些代码。Lombok 会创建一个与上述手动实现的 Builder 类类似的内部类,并提供同样的链式调用功能。

在这里插入图片描述


四、建造者模式的注意事项

  1. 避免滥用建造者模式:虽然建造者模式可以提供一种清晰和灵活的方式来构建复杂对象,但过度使用它可能会导致代码变得复杂和难以维护。因此,在决定使用建造者模式之前,应该仔细评估对象的复杂性和构建过程的需求。

  2. 考虑建造者的可重用性:如果对象的构建过程涉及一些可重用的步骤或组件,那么可以设计可重用的建造者类来减少代码重复和提高可维护性。例如,可以创建一些通用的建造者类来处理常见的构建步骤,并在需要时进行扩展或定制。

  3. 注意与工厂模式的区别:建造者模式与工厂模式都是创建型设计模式,但它们的职责和用途有所不同。工厂模式主要负责对象的创建和实例化,而建造者模式则关注于对象的构建过程和属性的设置。在实际应用中,可以根据需要选择使用哪种模式或结合使用它们。

五、结语

  • 建造者模式是一种强大且灵活的设计模式,适用于构建具有多个属性和复杂构建过程的对象。
  • 通过合理使用建造者模式,可以提高代码的可读性、可维护性和可扩展性。
  • 然而,在使用建造者模式时,也需要注意避免滥用、考虑线程安全问题以及与其他设计模式的区别和协作。
  • 只有在合适的场景下使用建造者模式,才能充分发挥其优势并提升软件的质量。

经典建造者模式和流式建造者模式都是用于封装复杂对象创建过程的有效方式。经典建造者模式通过抽象建造者接口和具体建造者类的分离,实现了构建过程的灵活性和可扩展性。而流式建造者模式则通过链式调用的方式,提高了代码的可读性和编写的便捷性。

在选择使用哪种建造者模式时,需要根据具体的需求和场景来判断。如果需要较高的灵活性和可扩展性,可以考虑使用经典建造者模式;如果追求代码的简洁和易读性,流式建造者模式可能是一个更好的选择。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

图分割 Graph Partition 学习笔记1

文章目录 前言一、graph-partition是什么?二、具体分类三、graph-partition的意义参考链接 前言 最近在学习图论划分的方法,碰巧搜索到了这个算是对我而言全新的一个体系,在这里将逐步记载自己的学习资料和进度,希望和大家一起探讨…

《汇编语言》第3版(王爽)实验9

第9章 实验9 编程:在屏幕中间分别显示绿色、绿底红色、白底蓝色的字符串 ‘welcome to masm!’ assume cs:code,ds:datadata segmentdb welcome to masm!,0 data endscode segmentstart:mov ax,data mov ds,ax ;ds指向data段mov ax,0B800H ;显存空间从B800H…

波奇学Linux: 信号捕捉

sigaction:修改信号对应的handler方法 act输入型参数&#xff0c;oldact输出型参数 void (*sa_handler) (int) //修改的自定义函数 sigset_t sa_mask // void handler(int signo) {cout<<"catch a signal, signal number: "<<signo<<endl; } int …

C#实现快速排序算法

C#实现快速排序算法 以下是C#中的快速排序算法实现示例&#xff1a; using System;class QuickSort {// 快速排序入口函数public static void Sort(int[] array){QuickSortRecursive(array, 0, array.Length - 1);}// 递归函数实现快速排序private static void QuickSortRecu…

记录一次排查负载均衡不能创建的排查过程

故障现象&#xff0c;某云上&#xff0c;运维同事在创建负载均衡的时候&#xff0c;发现可以创建资源&#xff0c;但是创建完之后&#xff0c;不显示对应的负载均衡。 创建负载均衡时候&#xff0c;按f12发现console有如下报错 后来请后端网络同事排查日志发现&#xff0c;是后…

机器学习--循环神经网络(RNN)3

本篇文章结合具体的例子来介绍一下LSTM运算方式以及原理。请结合上篇文章的介绍食用。 一、具体例子 如上图所示&#xff0c;网络里面只有一个 LSTM 的单元&#xff0c;输入都是三维的向量&#xff0c;输出都是一维的输出。 这三维的向量跟输出还有记忆元的关系是这样的。 假设…

【蓝桥杯】k倍区间

一.题目描述 二.问题分析 对于该问题&#xff0c;标签上写的是暴力&#xff0c;但是如果使用暴力的话&#xff0c;会超时。 首先&#xff0c;对于两个数a&#xff0c;b&#xff08;假设a小于b&#xff09;&#xff0c;若a与b对k取余后结果相同&#xff0c;则b-a可以整除k。 …

Edu 18 Colored balls -- 题解

目录 Colored Balls&#xff1a; 题目大意: 思路解析&#xff1a; 代码实现&#xff1a; Colored Balls&#xff1a; 题目大意: 思路解析&#xff1a; 我们对于一个数n&#xff0c;如果分组大小超过了 根号n&#xff0c;那么便不可能将n 分为多个组&#xff0c;并且组间差距…

苍穹外卖学习-----2024/03/09

1.菜品分页查询 代码在这里 分页查询菜品 2.删除菜品 [链接]param 1、概览 本文将带你了解 Spring 中 RequestParam 注解的用法。 简单地说&#xff0c;可以使用 RequestParam 从请求中提取查询参数、表单参数甚至是多个参数。 2、示例端点 假设我们有一个端点 /api/foos&a…

qt一个项目只能有一个QMainWindow,其他小窗口只能继承QWidget

我继承QMainWindow&#xff0c;结果就出现奇奇怪怪的现象&#xff0c;我人都疯了 这些接口全他妈不能用 删了换成QWidget就可以用了

重建大师下图界面上的颜色区域分别代表着什么?

分别代表相对精度、绝对精度。 重建大师是一款专为超大规模实景三维数据生产而设计的集群并行处理软件&#xff0c;输入倾斜照片&#xff0c;激光点云&#xff0c;POS信息及像控点&#xff0c;输出高精度彩色网格模型&#xff0c;可一键完成空三、自动建模和LOD构建。 下载地…

idea连接远程服务器

1. 双击shift&#xff0c;出现如下界面 2. 远程连接 原文来自这个up主的&#xff0c;点击蓝色字体就可以跳转啦&#xff01; 输入主机ip、用户名、密码&#xff0c;点击Test Connection验证&#xff0c;最后点击ok添加成功 有用的话记得给俺点个赞&#xff0c;靴靴~

赶紧来学Python回调函数

在Python中&#xff0c;回调函数是一种非常重要的编程概念&#xff0c;它允许我们将一个函数作为参数传递给另一个函数&#xff0c;并在需要时由另一个函数调用。回调函数的使用可以使代码更加灵活和可重用&#xff0c;尤其在异步编程、事件驱动编程中非常常用。 1.普通函数调…

波动数列 刷题笔记

思路分析 dp 找出状态转移方程 设d为a或者-b 代码 #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int N1010,MOD100000007; int get_mod(int a,int b){ return (a%bb)%b; …

悲观锁和乐观锁

悲观锁&#xff1a;比较悲观&#xff0c;认为线程安全问题一定会发生&#xff0c;因此在操作数据之前先获取锁&#xff0c;确保线程串行执行。-例如Synchronized、Lock都属于悲观锁。 乐观锁&#xff1a;比较乐观&#xff0c;认为线程安全问题不一定会发生&#xff0c;因此不加…

数论<1>——数论基础

这期博客是一个数论入门介绍&#xff0c;dalao们可以自动忽略。 Part 1:素数(质数) 说到数论&#xff0c;小学奥数里也有。我最先想到的就是质数了。素数就是一个只能被1和它自己整除的数。判断的方法也很简单&#xff0c;可以扫一遍就结束了&#xff0c;但是没必要。由于一个…

使用yarn创建vite+vue3electron多端运行

文章目录 第一步 使用yarn创建vite+vue3项目遇到创建报错看第二步 引入electron第三步 创建main.js在electron下面的main.js写入下面代码第四步 安装同时运行多条命令npm包&&修改package.json文件npm包增加一条electron运行脚本命令效果图第一步 使用yarn创建vite+vue3…

[Electron]中IPC进程间通信

Electron中IPC 进程间通信 (IPC) 是在 Electron 中构建功能丰富的桌面应用程序的关键部分之一。在 Electron 中&#xff0c;进程使用 ipcMain 和 ipcRenderer 模块&#xff0c;通过开发人员定义的“通道”传递消息来进行通信。 本文介绍以下几个方面&#xff1a; 1-渲染进程到…

SpringCloud基础

SpringCloud基础环境 1、基本环境版本选择 Java&#xff1a; Java17&#xff1b;spring cloud&#xff1a;2023.0.0&#xff1b;spring boot&#xff1a;3.2.0&#xff1b;cloud alibaba&#xff1a;2022.0.0.0-RC2&#xff1b;Maven&#xff1a;3.9&#xff1b;Mysql&#x…

SQL设计时增加说明列

后关闭sql Studio,然后打开注册表,注册表地址: 计算机\HKEY_CURRENT_USER\SOFTWARE\Microsoft\SQL Server Management Studio\18.0_IsoShell\DataProject 如有版本不同,红色内容有所变化,修改内容如下: SSVPropViewColumnsSQL70,SSVPropViewColumnsSQL80 全修改为 1,2,6,7…