Java设计模式之建造者模式详细讲解和案例示范

news2024/9/20 9:46:08

引言

在软件开发中,创建复杂对象常常伴随着众多参数和配置,这种情况容易导致“构造函数爆炸”或参数混乱的问题。为了解决这一问题,建造者模式(Builder Pattern)应运而生。它通过分步骤地构建对象,提供了更灵活且易于阅读的对象创建方式。本文将详细探讨建造者模式,结合电商交易系统中的具体案例,深入讲解其使用场景、常见问题及解决方案。

1. 建造者模式概述

1.1 定义

建造者模式是一种创建型设计模式,允许用户通过一步步地构造复杂对象的方式来避免直接使用大量的构造函数或复杂的初始化过程。它通过将对象的创建过程分解为多个步骤,并允许这些步骤以链式调用的方式来执行,最终生成一个完全构建的对象。

1.2 结构

建造者模式通常由以下几个核心部分构成:

  • Builder接口:定义了创建对象各个部分的抽象步骤。
  • 具体建造者(ConcreteBuilder):实现了Builder接口,负责具体对象各部分的构建。
  • 产品(Product):表示最终构建的复杂对象。
  • 指挥者(Director):负责调用Builder中的构建步骤,以按顺序构建复杂对象。
1.3 适用场景

建造者模式适用于以下场景:

  • 需要构建的对象具有复杂的内部结构:如包含多个可选参数或依赖顺序的构造步骤。
  • 相同的构建过程可以创建不同的表示:即通过相同的步骤构建不同的对象。
  • 对象的创建步骤独立且复杂:需要将对象的创建过程进行解耦和分步骤处理。

2. 建造者模式在电商交易系统中的应用

在电商交易系统中,订单、购物车、产品详情等对象可能涉及到多个可选参数,并且这些参数可能来自不同的来源。在这种情况下,建造者模式可以帮助我们简化对象的创建过程,使代码更具可读性和可维护性。

2.1 使用建造者模式构建订单对象

我们以订单对象为例,展示如何在电商系统中使用建造者模式。

2.1.1 订单类定义

首先,我们定义一个Order类,该类包括了多个字段,如订单ID、客户信息、商品列表、配送地址等。

public class Order {
    private String orderId;
    private String customerName;
    private List<String> items;
    private String shippingAddress;
    private boolean isGift;
    private String deliveryInstructions;

    private Order(OrderBuilder builder) {
        this.orderId = builder.orderId;
        this.customerName = builder.customerName;
        this.items = builder.items;
        this.shippingAddress = builder.shippingAddress;
        this.isGift = builder.isGift;
        this.deliveryInstructions = builder.deliveryInstructions;
    }

    public static class OrderBuilder {
        private String orderId;
        private String customerName;
        private List<String> items = new ArrayList<>();
        private String shippingAddress;
        private boolean isGift;
        private String deliveryInstructions;

        public OrderBuilder(String orderId, String customerName) {
            this.orderId = orderId;
            this.customerName = customerName;
        }

        public OrderBuilder addItem(String item) {
            this.items.add(item);
            return this;
        }

        public OrderBuilder setShippingAddress(String shippingAddress) {
            this.shippingAddress = shippingAddress;
            return this;
        }

        public OrderBuilder setIsGift(boolean isGift) {
            this.isGift = isGift;
            return this;
        }

        public OrderBuilder setDeliveryInstructions(String deliveryInstructions) {
            this.deliveryInstructions = deliveryInstructions;
            return this;
        }

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

    // Getter方法省略...
}

在这个例子中,OrderBuilder类提供了构建Order对象所需的各个方法,每个方法返回OrderBuilder实例,以便进行链式调用。最后,build方法生成并返回最终的Order对象。

2.1.2 使用建造者模式创建订单

接下来,我们展示如何通过建造者模式来创建订单对象。

public class ECommerceApp {
    public static void main(String[] args) {
        Order order = new Order.OrderBuilder("12345", "Alice")
            .addItem("Laptop")
            .addItem("Mouse")
            .setShippingAddress("123 Main St")
            .setIsGift(true)
            .setDeliveryInstructions("Leave at front door")
            .build();

        System.out.println("Order ID: " + order.getOrderId());
        System.out.println("Customer Name: " + order.getCustomerName());
        System.out.println("Items: " + order.getItems());
        System.out.println("Shipping Address: " + order.getShippingAddress());
    }
}

通过链式调用,我们可以灵活地构建一个订单对象,并且代码清晰且易于理解。

2.1.3 类图

在这里插入图片描述

3. 常见问题与解决方案

虽然建造者模式在对象构建时提供了极大的灵活性和清晰性,但在实际应用中,仍然可能遇到一些常见问题。以下是几个常见问题及其解决方案。

3.1 必填参数与可选参数

问题:有时对象构建需要一些必填参数,这些参数必须在对象创建时提供,而其他参数则是可选的。

解决方案:在建造者模式中,我们可以通过在OrderBuilder的构造函数中传入必填参数,将其强制设置为必需值。这样可以保证对象的创建过程符合业务逻辑要求。

public OrderBuilder(String orderId, String customerName) {
    this.orderId = orderId;
    this.customerName = customerName;
}
3.2 不可变对象

问题:有时我们需要构建一个不可变对象,即一旦对象创建完成,其内部状态不可再被修改。

解决方案:建造者模式非常适合创建不可变对象。我们可以通过确保所有字段都是final类型,并且没有提供任何修改对象状态的方法来实现这一点。

public class Order {
    private final String orderId;
    private final String customerName;
    private final List<String> items;
    private final String shippingAddress;
    private final boolean isGift;
    private final String deliveryInstructions;

    // 构造函数和OrderBuilder类实现省略...
}
3.3 多步构建过程

问题:某些复杂对象的构建过程可能包含多个步骤或多个阶段,需要在不同时间点进行构建。

解决方案:建造者模式可以通过在不同时间点调用build方法生成中间状态的对象,然后继续构建,最终完成对象创建。

4. 建造者模式的优势与劣势

4.1 优势
  • 减少构造函数的数量:避免了“构造函数爆炸”的问题,特别是在有许多可选参数时。
  • 增强代码的可读性:链式调用方式使得代码更加清晰易读,易于理解对象的构建过程。
  • 灵活的对象创建:可以根据需要选择性地设置属性,支持复杂对象的分步构建。
4.2 劣势
  • 代码量增加:建造者模式通常会增加代码的复杂性,因为需要定义多个构建方法及其链式调用支持。
  • 可能带来性能开销:在一些性能敏感的场景中,频繁调用建造者的链式方法可能带来一定的性能开销。

5. 建造者模式在开源框架中的应用

建造者模式在Java的许多开源框架中得到了广泛应用,特别是在需要创建复杂配置对象或需要流式API的场景中。以下是几个典型的例子

5.1 建造者模式在Lombok中的应用

Lombok是一个广受欢迎的Java库,它通过注解的方式简化了Java代码的编写。Lombok中提供的@Builder注解是建造者模式的典型实现之一,它可以自动生成Builder类,极大地简化了对象的创建过程。

5.1.1 Lombok中的@Builder注解

Lombok的@Builder注解可以应用于类、构造函数或方法上,生成对应的Builder类。我们来看一个具体的例子。

假设我们仍然使用前面提到的Order类,在Lombok的帮助下,通过@Builder注解可以极大地简化代码。

5.1.2 使用Lombok的@Builder注解创建订单对象
import lombok.Builder;
import lombok.ToString;

import java.util.List;

@Builder
@ToString
public class Order {
    private String orderId;
    private String customerName;
    private List<String> items;
    private String shippingAddress;
    private boolean isGift;
    private String deliveryInstructions;
}

在这个例子中,@Builder注解自动生成了一个OrderBuilder类,包含了所有必要的构建方法。你不需要手动编写这些方法,只需通过Lombok自动生成。

接下来,我们展示如何使用生成的Builder类创建订单对象。

import java.util.Arrays;

public class ECommerceApp {
    public static void main(String[] args) {
        Order order = Order.builder()
            .orderId("12345")
            .customerName("Alice")
            .items(Arrays.asList("Laptop", "Mouse"))
            .shippingAddress("123 Main St")
            .isGift(true)
            .deliveryInstructions("Leave at front door")
            .build();

        System.out.println(order);
    }
}

Lombok的@Builder注解使得对象的创建过程更为简洁,而且代码看起来更清晰。Order.builder()会返回一个OrderBuilder对象,允许我们通过链式调用的方式设置各个属性,最后调用build()方法生成最终的Order对象。

5.1.3 使用场景

在实际项目中,Lombok的@Builder注解非常适合以下场景:

  1. 简化代码:当需要频繁创建复杂对象时,@Builder注解可以显著减少样板代码,尤其是在具有多个可选参数的类中。
  2. 增强可读性:通过Builder模式,代码的可读性得到了极大的提升,构造方法的链式调用使得代码更加直观。

5.2 建造者模式在Guava中的应用

除了Lombok之外,Google的Guava库也是Java开发中常用的一个工具库,其中的ImmutableList.Builder就是建造者模式的经典应用。

5.2.1 ImmutableList.Builder介绍

Guava中的ImmutableList是一个不可变的列表,实现了不可修改的列表结构。它的Builder类允许用户逐步构建一个不可变的列表,并在完成后一次性创建。

5.2.2 使用ImmutableList.Builder创建不可变列表
import com.google.common.collect.ImmutableList;

public class ECommerceApp {
    public static void main(String[] args) {
        ImmutableList<String> items = ImmutableList.<String>builder()
            .add("Laptop")
            .add("Mouse")
            .add("Keyboard")
            .build();

        System.out.println(items);
    }
}

在这个例子中,ImmutableList.Builder提供了链式调用的方式来添加元素,最终通过build()方法创建一个不可变的列表。

5.2.3 使用场景

Guava的ImmutableList.Builder适合以下场景:

  1. 创建不可变集合:当需要创建一个不可修改的集合时,ImmutableList.Builder提供了灵活且安全的方式。
  2. 避免多线程问题:不可变对象天生是线程安全的,使用ImmutableList.Builder可以在多线程环境中避免共享可变状态带来的问题。

结论

建造者模式通过分步骤创建复杂对象,提供了灵活且可读的对象构建方式。在Java的实际开发中,建造者模式被广泛应用于各种场景,特别是在对象初始化复杂、参数众多或需要创建不可变对象时。通过Lombok和Guava等开源库的实例,我们可以看到建造者模式在简化代码、增强可读性和避免常见问题方面的强大作用。

在未来的开发中,我们可以根据具体的需求选择合适的建造者模式实现方式,从而提高代码的质量和可维护性。

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

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

相关文章

C++编程:高阶编程

本阶段主要针对泛型编程和STL技术作详细讲解。 1 模板 1.1 模板的概念 C提供一种泛型编程&#xff0c;主要利用模板的技术&#xff0c;模板的目的是提高代码复用性&#xff0c;将类型参数化。 C提供两种模板机制&#xff1a; 函数模板类模板 模板的特点&#xff1a; 模板…

实习结束总结20240828

长达两个月的实习终于在今天结束了&#xff0c;不知怎的&#xff0c;心如止水&#xff0c;没有高兴&#xff0c;没有伤心&#xff0c;毫无波澜的内心甚至让自己都感觉可怕&#xff0c;也许&#xff0c;这就是成长吧。 硬件上&#xff1a; 1.cadence需要继续深入学习&#xff…

Transformers for One-Shot Visual Imitation

发表时间&#xff1a;(CoRL 2020&#xff09; 论文链接&#xff1a;https://readpaper.com/pdf-annotate/note?pdfId4546337571360890881&noteId2424798489658874880 作者单位&#xff1a;Carnegie Mellon University, USA Motivation&#xff1a;人类能够通过推断他们…

每日OJ_牛客_年会抽奖(错排问题)

牛客_年会抽奖&#xff08;错排问题&#xff09; 年会抽奖__牛客网 解析代码 该题为经典的错排问题 用A、B、C……表示写着&#xff4e;位友人名字的信封&#xff0c;a、b、c……表示&#xff4e;份相应的写好的信纸。把错装的总数为记作 D(n)。假设把&#xff41;错装进&…

【ubuntu使用笔记】Ubuntu Desktop 访问SMB共享文件夹

Ubuntu Desktop 访问SMB共享文件夹 Ubuntu Desktop 访问SMB共享文件夹文件夹打开 file managerother location输入 IP地址&#xff0c;smb://IP点击connect按钮正常进入 命令行安装客户端连接 Ubuntu Desktop 访问SMB共享文件夹 文件夹 打开 file manager other location 输入…

WATCH, TRY, LEARN: META-LEARNING FROM DEMONSTRATIONS AND REWARDS

&#xff08;感觉有点从失败中学习的意思&#xff09; 发表时间&#xff1a;30 Jan 2020 论文链接&#xff1a;https://readpaper.com/pdf-annotate/note?pdfId4545005537963171841&noteId2453371997770644736 作者单位&#xff1a;UC Berkeley Motivation&#xff1a…

2024年高教社杯全国大学生数学建模竞赛

2024年高教社杯全国大学生数学建模竞赛通知 2024数模比赛确定时间为&#xff1a;9月5日18时至9月8日20时 报名截止日期&#xff1a;9月2日&#xff08;周一&#xff09;20时

使用maven 实现版本覆盖案例4【经典版】

一 原理介绍 1.1 原理逻辑 关系图&#xff1a;从使用方xinxiang_demo中引用A_parent的2.13的版本的层级要比B_Module继承A_parent&#xff1a;1.1 版本的层级要短&#xff0c;优先级更高&#xff0c;所以使用2.13&#xff0c;将B_Module继承A_parent 1.1 版本覆盖。 1.2 实操…

约 数个数

对于一个数 其中&#xff1a;是的各个质因数&#xff0c;上式是的质因数乘积式。 约数个数&#xff1a; 约数之和&#xff1a; step1&#xff1a; 采用分解质因数的方法&#xff0c;计算出的每一个质因数的次数 &#xff08;分解质因数的blog&#xff1a;http://t.csdni…

Kali学习(ms17-010、ms08-067漏洞复现)

目录 一、kali网络设置 NAT模式、桥接上网/仅主机 VMnet0、VMnet1、VMnet8 1.NAT模式 VMnet8 &#xff08;1&#xff09;检查服务 &#xff08;2&#xff09;创建虚拟网卡 &#xff08;3&#xff09;创建kali的网卡信息 &#xff08;4&#xff09;验证 2.桥接模式 VMn…

结果一。5.be doing表将来和 表 will的区别

be doing 表⽰近期、眼下就要发⽣的事情; will 表⽰将来的时间,则较远⼀些。如: He is going to write a letter tonight.He will write a book 。 be going to 表⽰根据主观判断将来肯定发⽣的事情。 will+ 动词原形表⽰⼀般将来时。 will ࿰

如何用Java SpringBoot+Vue打造高效产品订单管理系统?

&#x1f34a;作者&#xff1a;计算机毕设匠心工作室 &#x1f34a;简介&#xff1a;毕业后就一直专业从事计算机软件程序开发&#xff0c;至今也有8年工作经验。擅长Java、Python、微信小程序、安卓、大数据、PHP、.NET|C#、Golang等。 擅长&#xff1a;按照需求定制化开发项目…

【C++ Primer Plus习题】6.9

问题: 解答: #include <iostream> #include <string> #include <fstream> using namespace std;typedef struct _Donor {string name;double money; }Donor;int main() {ifstream file;string filename;int count 0;cout << "请输入文件名:&quo…

PDF合并可以如此简单?!这3种方法,简单又实用!

在日常的忙碌与学习中&#xff0c;我们时常需要将散落的PDF文件整合成一份完整的文档&#xff0c;无论是为了整理报告、汇编学习资料&#xff0c;还是为了提升阅读效率&#xff0c;PDF合并都显得尤为重要。 今天&#xff0c;就让我带你解锁三种既简单又高效的PDF合并方法&#…

进程终止 等待 替换

文章目录 一.进程的终止进程终止实在做什么&#xff1f;进程终止的3种情况自定义退出码 如何终止进程&#xff1f; 二.进程等待为什么要进行进程等待&#xff1f;进程如何等待&#xff1f;waitwaitpid 阻塞等待 && 非阻塞等待 三.进程的程序替换先看代码 && 现…

Windows电脑还在纠结用什么便签软件?

在快节奏的生活中&#xff0c;我们常常需要记录下一些重要的事情或者临时的想法&#xff0c;而便签软件就是我们的好帮手。但是&#xff0c;面对市面上众多的便签软件&#xff0c;Windows电脑用户可能会感到困惑&#xff0c;不知道该如何选择。在这里&#xff0c;小编为你提供一…

Great Wall长城工作站安装银河麒麟V10(SP1)-ARM版桌面操作系统

长城工作站安装银河麒麟V10(SP1)桌面操作系统 1. 硬件信息 [1]. Great Wall 长城台式微型计算机 产品型号&#xff1a;世恒TD120A2 型号代码&#xff1a;世恒TD120A2-019 电源&#xff1a;220V~3A 50Hz [2]. 芯片型号 架构&#xff1a; aarch64 CPU 运行模式&#xff1a…

【Qt窗口】—— 浮动窗口

目录 1.1 浮动窗口的创建 1.2 设置停靠的位置 1.3 示例小结 在Qt中&#xff0c;浮动窗口也称之为铆接部件&#xff0c;俗称为子窗口&#xff0c;浮动窗口是通过QDockWidget类来实现浮动的功能。浮动窗口⼀般是位于核⼼部件的周围&#xff0c;可以有多个。 1.1 浮动窗口的…

LeetCode 热题100-39 对称二叉树

对称二叉树 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,null,3] 输出&#xff1a;false提示&#xff1a; 树中…

【python报错已解决】AttributeError: module ‘PIL.Image‘ has no attribute ‘ANTIALIAS‘

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏: 《C干货基地》《粉丝福利》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 引言 当我们使用某些Python库&#xff0c;如Pillow&#xff08;PIL的一个分支&#xff09;&#xff0c;进行图像处理时&#x…