【设计模式】创建型-建造者模式

前言

在面向对象的软件开发中,构建复杂对象时经常会遇到许多挑战。一种常见的解决方案是使用设计模式,其中建造者模式是一个强大而灵活的选择。本文将深入探讨建造者模式的原理、结构、优点以及如何在实际项目中应用它。

一、复杂的对象

public class Computer {
    private String ram;
    private String hardDisk;
    private String cpu;
    // ...可能还有更多参数...

    public Computer(String ram, String hardDisk, String cpu) {
        this.ram = ram;
        this.hardDisk = hardDisk;
        this.cpu = cpu;
        // ...初始化更多参数...
    }

    // getters and setters...
}

// 使用构造函数创建对象,参数多且容易混淆
Computer computer = new Computer("16GB", "1TB", "Intel i7");

上面代码比较简单,但是如果 Computer 类有更多的参数,构造函数将会变得非常长,而且在创建 Computer 对象时,传递参数的顺序非常重要,一旦顺序错误,就会创建出一个配置错误的对象。此外,如果参数有默认值,那么用户还需要记住哪些参数是必须的,哪些是可选的,这增加了使用的复杂性。

我们可以总结出创建复杂对象时可能会遇到的问题包括:

  1. 参数过多:构造函数或者 setter 方法的参数可能会非常多,导致代码难以阅读和维护。
  2. 参数顺序:容易混淆参数的顺序,特别是当有多个相同类型的参数时。
  3. 不够灵活:如果创建过程中需要多个步骤,使用构造函数或 setter 方法就不够灵活。
  4. 不可读性:代码的可读性差,特别是在没有注释的情况下,很难理解每个参数的意义。

二、建造者模式

建造者模式是一种创建型设计模式,旨在将复杂对象的构建过程与其表示分离。通过使用建造者模式,可以使客户端代码与对象的内部结构解耦,从而使构建过程更加灵活,并且更易于维护和扩展。

三、建造者模式的核心组成部分

在这里插入图片描述

建造者模式通常包括以下几个关键组件:

  1. 产品(Product):表示被构建的复杂对象。产品类通常包含多个属性,并且可能包含一些复杂的业务逻辑。
  2. 抽象建造者(Builder):定义了构建产品所需的接口。抽象建造者通常包括一系列方法来构建产品的各个部分。
  3. 具体建造者(Concrete Builder):实现了抽象建造者接口,负责实际构建产品的各个部分,并提供方法来获取最终的产品实例。
  4. 指挥者(Director):负责调用建造者的方法来构建产品,但不直接创建产品的实例。指挥者通常根据一定的构建步骤来组织构建过程。

四、运用建造者模式

场景假设:我们要构建一台计算机,它有不同的部件,比如 CPU、内存、硬盘等。我们将使用建造者模式来构建这台计算机。

  1. 定义产品类: 首先,确定需要构建的复杂对象的属性和方法,并创建相应的产品类。这个产品类应该包含对象的所有属性,并提供相应的 getter 和 setter 方法。

    /**
     * 产品类:计算机
     */
    public class Computer {
        private String cpu;       // CPU
        private String memory;    // 内存
        private String hardDisk;  // 硬盘
    
        // 省略构造函数和 getter/setter方法
    }
    
  2. 创建抽象建造者接口: 定义一个抽象建造者接口,该接口包含构建产品各个部件的抽象方法。这些方法代表构建产品所需的不同步骤。

    /**
     * 抽象建造者接口
     */
    public interface ComputerBuilder {
        // 构建CPU
        void buildCPU();
    
        // 构建内存
        void buildMemory();
    
        // 构建硬盘
        void buildHardDisk();
    
        // 构建计算机
        Computer build();
    }
    
  3. 实现具体建造者类: 创建一个或多个具体建造者类,实现抽象建造者接口。每个具体建造者类负责实现构建产品各个部件的具体方法,并在最后返回构建好的产品。

    /**
     * 具体建造者类:桌面计算机建造者
     */
    public class DesktopComputerBuilder implements ComputerBuilder {
        private Computer computer;  // 待构建的计算机对象
    
        public DesktopComputerBuilder() {
            this.computer = new Computer();
        }
    
        @Override
        public void buildCPU() {
            computer.setCpu("Intel Core i7");
        }
    
        @Override
        public void buildMemory() {
            computer.setMemory("16GB DDR4");
        }
    
        @Override
        public void buildHardDisk() {
            computer.setHardDisk("1TB SSD");
        }
    
        @Override
        public Computer build() {
            // 返回构建好的计算机对象
            return computer;
        }
    }
    
  4. 创建指挥者类: 定义一个指挥者类,该类负责使用具体建造者对象构建最终的产品。指挥者类知道构建者的具体实现细节,但与产品的实际构建过程无关。

    /**
     * 指挥者类:计算机指挥者
     */
    public class ComputerDirector {
        private ComputerBuilder computerBuilder;  // 建造者对象
    
        public ComputerDirector(ComputerBuilder computerBuilder) {
            this.computerBuilder = computerBuilder;
        }
    
        // 使用建造者构建计算机对象
        public Computer construct() {
            // 按照顺序调用建造者的方法来构建计算机
            computerBuilder.buildCPU();
            computerBuilder.buildMemory();
            computerBuilder.buildHardDisk();
            // 返回构建好的计算机对象
            return computerBuilder.build();
        }
    }
    
  5. 使用建造者模式构建对象: 在客户端代码中,创建具体的建造者对象,并将其传递给指挥者类。然后,通过指挥者类调用相应的方法来构建产品。最终,客户端代码可以获取构建好的产品并使用它。

    public class Main {
        public static void main(String[] args) {
            // 创建桌面计算机的建造者对象
            ComputerBuilder desktopBuilder = new DesktopComputerBuilder();
            // 创建计算机指挥者对象,并传入桌面计算机的建造者
            ComputerDirector director = new ComputerDirector(desktopBuilder);
            // 使用指挥者构建计算机对象
            Computer desktop = director.construct();
            // 输出构建好的桌面计算机的配置信息
            System.out.println("Desktop Computer Configuration:");
            System.out.println(desktop);
        }
    }
    
    /*
        在一些简单的情况下,可以省略指挥者(Director)类。
        特别是当只有一个具体建造者(Concrete Builder)时,
        客户端代码可以直接调用具体建造者的方法来构建产品,而不需要指挥者类。
    */
    public class Main {
        public static void main(String[] args) {
            // 创建桌面计算机的建造者对象
            ComputerBuilder desktopBuilder = new DesktopComputerBuilder();
            // 使用桌面计算机的建造者直接构建计算机对象
            Computer desktop = desktopBuilder.buildCPU()
                                            .buildMemory()
                                            .buildHardDisk()
                                            .build();
            // 输出构建好的桌面计算机的配置信息
            System.out.println("Desktop Computer Configuration:");
            System.out.println(desktop);
        }
    }
    

在上面的例子中,我们定义了 Computer 类作为产品,ComputerBuilder 作为具体建造者来创建产品,ComputerDirector 作为指导者确定创建产品时遵循的步骤流程。客户端代码只需通过 ComputerDirector 便能构建 Computer 对象,这样就隐藏了构建细节,使得客户端代码更加简洁和易于维护。

五、建造者模式的应用场景

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

  1. 复杂对象的创建:当创建的对象非常复杂,包含多个组成部分时,建造者模式可以帮助管理复杂性,使得代码更加清晰。

    // 假设我们需要创建一个复杂的 Pizza 对象,它包含多种配料和选项。
    // 产品类
    public class Pizza {
        private String dough;
        private String sauce;
        private String topping;
    
        // 私有构造器
        private Pizza(Builder builder) {
            this.dough = builder.dough;
            this.sauce = builder.sauce;
            this.topping = builder.topping;
        }
    
        // Builder类
        public static class Builder {
            private String dough;
            private String sauce;
            private String topping;
    
            public Builder withDough(String dough) {
                this.dough = dough;
                return this;
            }
    
            public Builder withSauce(String sauce) {
                this.sauce = sauce;
                return this;
            }
    
            public Builder withTopping(String topping) {
                this.topping = topping;
                return this;
            }
    
            public Pizza build() {
                return new Pizza(this);
            }
        }
    }
    
    // 客户端代码
    // 在创建 Pizza 时,可以根据需要选择是否设置 dough、sauce、topping
    Pizza pizza = new Pizza.Builder()
                    .withDough("cross")
                    .withSauce("mild")
                    .withTopping("ham and pineapple")
                    .build();
    
  2. 构造过程需要分步骤进行:如果一个对象的构造过程需要分多个步骤或者阶段来完成,建造者模式允许你逐步构造对象,而不是一次性通过一个巨大的构造函数完成。

    // 倘若 House 对象的构建,需要分步骤设置地基、结构、屋顶和内部装修。
    // 产品类
    public class House {
        private String foundation;
        private String structure;
        private String roof;
        private String interior;
    
        private House(Builder builder) {
            this.foundation = builder.foundation;
            this.structure = builder.structure;
            this.roof = builder.roof;
            this.interior = builder.interior;
        }
    
        // Builder类
        public static class Builder {
            private String foundation;
            private String structure;
            private String roof;
            private String interior;
    
            public Builder withFoundation(String foundation) {
                this.foundation = foundation;
                return this;
            }
    
            public Builder withStructure(String structure) {
                this.structure = structure;
                return this;
            }
    
            public Builder withRoof(String roof) {
                this.roof = roof;
                return this;
            }
    
            public Builder withInterior(String interior) {
                this.interior = interior;
                return this;
            }
    
            public House build() {
                return new House(this);
            }
        }
    }
    
    // 客户端代码
    // 在创建对象之前,可严格管控步骤的先后顺序
    House house = new House.Builder()
                    .withFoundation("concrete")
                    .withStructure("wooden")
                    .withRoof("shingle")
                    .withInterior("painted")
                    .build();
    
  3. 同一构建过程不同表示:当需要根据不同的需求和过程来创建不同的对象表示时,建造者模式提供了很好的解决方案。

    // 如果我们有一个 Car 对象,它可以有不同的配置,例如经济型和豪华型。
    // 产品类
    public class Car {
        private String engine;
        private String seats;
        private String navigationSystem;
    
        private Car(Builder builder) {
            this.engine = builder.engine;
            this.seats = builder.seats;
            this.navigationSystem = builder.navigationSystem;
        }
    
        // Builder类
        public static class Builder {
            private String engine;
            private String seats;
            private String navigationSystem;
    
            public Builder withEngine(String engine) {
                this.engine = engine;
                return this;
            }
    
            public Builder withSeats(String seats) {
                this.seats = seats;
                return this;
            }
    
            public Builder withNavigationSystem(String navigationSystem) {
                this.navigationSystem = navigationSystem;
                return this;
            }
    
            public Car build() {
                return new Car(this);
            }
        }
    }
    
    // 客户端代码
    // 根据不同配置,满足不同需求
    Car economyCar = new Car.Builder()
                        .withEngine("1.5L")
                        .withSeats("cloth")
                        .build();
    
    Car luxuryCar = new Car.Builder()
                        .withEngine("3.0L V6")
                        .withSeats("leather")
                        .withNavigationSystem("advanced")
                        .build();
    
  4. 参数多且复杂:当构造函数的参数非常多,且有些可能是可选的,建造者模式可以帮助组织这些参数,使得构造函数不会过于庞大和复杂。

    // 倘若 Order 对象可能包含多个可选的配置项,如礼品包装、快递服务、优惠券等,
    // 使用建造者模式可以让客户端代码清晰地指定所需的配置。
    // 产品类:具有多个配置参数,其中一些是可选的。
    public class Order {
        private String product;
        private boolean giftWrap;
        private boolean expressDelivery;
        private String couponCode;
    
        // 私有构造器,只能通过Builder类构建Order对象
        private Order(Builder builder) {
            this.product = builder.product;
            this.giftWrap = builder.giftWrap;
            this.expressDelivery = builder.expressDelivery;
            this.couponCode = builder.couponCode;
        }
    
        // Builder 类:提供了一个链式 API 来设置 Order 对象的属性
        public static class Builder {
            private String product;
            private boolean giftWrap;
            private boolean expressDelivery;
            private String couponCode;
    
            public Builder(String product) {
                this.product = product;
            }
    
            public Builder setGiftWrap(boolean giftWrap) {
                this.giftWrap = giftWrap;
                return this;
            }
    
            public Builder setExpressDelivery(boolean expressDelivery) {
                this.expressDelivery = expressDelivery;
                return this;
            }
    
            public Builder setCouponCode(String couponCode) {
                this.couponCode = couponCode;
                return this;
            }
    
            // 构建方法:创建一个Order对象并返回
            public Order build() {
                return new Order(this);
            }
        }
    }
    
    // 客户端使用:创建一个 Order 对象
    Order order = new Order.Builder("Product1")
                    .setGiftWrap(true)
                    .setExpressDelivery(true)
                    .setCouponCode("DISCOUNT10")
                    .build();
    

六、小结

建造者模式是一种创建复杂对象的有效方式,它允许我们按照步骤构建对象,从而使得构建过程更加灵活和可配置。例如,在 Java 中,StringBuilder 是建造者模式的一个典型应用,它允许我们通过多个方法调用来构建最终的字符串,而不是一次性传入所有的字符串内容。另一个例子是流式 API,如 Java 8 的 Stream API,它允许通过链式调用来构建复杂的查询。通过将构建过程与表示分离,建造者模式提高了代码的可读性和可维护性,同时也使得代码更加易于扩展和重用。

推荐阅读

  1. Spring 三级缓存
  2. 深入了解 MyBatis 插件:定制化你的持久层框架
  3. Zookeeper 注册中心:单机部署
  4. 【JavaScript】探索 JavaScript 中的解构赋值
  5. 深入理解 JavaScript 中的 Promise、async 和 await

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

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

相关文章

安卓如何书写注册和登录界面

一、如何跳转一个活动 左边的是本活动名称, 右边的是跳转界面活动名称 Intent intent new Intent(LoginActivity.this, RegisterActivity.class); startActivity(intent); finish(); 二、如果在不同的界面传递参数 //发送消息 SharedPreferences sharedPreferen…

【再探】设计模式—中介者模式、观察者模式及模板方法模式

中介者模式让多对多的复杂引用关系变成一对多,同时能通过中间类来封装多个类中的行为,观察者模式在目标状态更新时能自动通知给订阅者,模版方法模式则是控制方法的执行顺序,子类在不改变算法的结构基础上可以扩展功能实现。 1 中…

Python 之SQLAlchemy使用详细说明

目录 1、SQLAlchemy 1.1、ORM概述 1.2、SQLAlchemy概述 1.3、SQLAlchemy的组成部分 1.4、SQLAlchemy的使用 1.4.1、安装 1.4.2、创建数据库连接 1.4.3、执行原生SQL语句 1.4.4、映射已存在的表 1.4.5、创建表 1.4.5.1、创建表的两种方式 1、使用 Table 类直接创建表…

【稳定检索/投稿优惠】2024年商务、信息管理与大数据经济国际会议(BIMBDE 2024)

2024 International Conference on Business, Information Management, and Big Data Economy 2024年商务、信息管理与大数据经济国际会议 【会议信息】 会议简称:BIMBDE 2024 大会地点:中国北京 会议官网:www.bimbde.com 会议邮箱&#xff…

MySql part1 安装和介绍

MySql part1 安装和介绍 数据 介绍 什么是数据库,数据很好理解,一般来说数据通常是我们所认识的 描述事物的符号记录, 可以是数字、 文字、图形、图像、声音、语言等,数据有多种形式,它们都以经过数字化后存入计算机…

CS4344国产替代音频DAC数模转换芯片DP7344采样率192kHz

目录 DAC应用简介DP7344简介结构框图DP7344主要特性微信号:dnsj5343参考原理图 应用领域 DAC应用简介 DAC(中文:数字模拟转换器)是一种将数字信号转换为模拟信号(以电流、电压或电荷的形式)的设备。电脑对…

Golang | Leetcode Golang题解之第123题买卖股票的最佳时机III

题目&#xff1a; 题解&#xff1a; func maxProfit(prices []int) int {buy1, sell1 : -prices[0], 0buy2, sell2 : -prices[0], 0for i : 1; i < len(prices); i {buy1 max(buy1, -prices[i])sell1 max(sell1, buy1prices[i])buy2 max(buy2, sell1-prices[i])sell2 m…

Docker 环境下 3D Guassian Splatting 的编译和配置

Title: Docker 环境下 3D Guassian Splatting 的编译和配置 文章目录 前言I. 宿主系统上的安装配置1. 安装 nvidia driver2. 安装 docker3. 安装 nvidia-container-toolkit II. Docker 容器安装配置1. 拉取 ubuntu 22.042. 创建容器3. 进入容器4. 容器中安装 cuda SDK5. 容器中…

python class __new__、__init__、__call__ 区别

在Python中&#xff0c;__new__、__init__ 和 __call__ 是三个不同的特殊方法&#xff0c;它们在类的创建和调用过程中扮演着不同的角色。以下是它们的区别和用法&#xff1a; 1. __new__ 方法 作用&#xff1a;__new__ 是一个静态方法&#xff0c;负责创建并返回一个新的实例…

携手亚马逊云科技,神州泰岳如何打通生成式AI落地最后三公里

导读&#xff1a;神州泰岳成为首批获得亚马逊云科技生成式AI能力认证的合作伙伴。 “过去6年来&#xff0c;在与亚马逊云科技的合作过程中&#xff0c;我们大概签约了300家以上的中国出海企业。”近日在一次沟通会上&#xff0c;神州泰岳副总裁兼云事业部总经理刘家歆这样向媒…

idea中使用maven-helper插件阅读排查【经典版】2

一 maven-helper的使用 1.1 helper页面 打开pom文件&#xff0c;并可以切换tab&#xff0c;简单使用&#xff0c;如下图&#xff1a; Conflicts&#xff08;查看冲突&#xff09; All Dependencies as List&#xff08;列表形式查看所有依赖&#xff09; All Dependencies …

【vue3|第4期】Vue3的选项式与组合式

日期&#xff1a;2024年5月30日 作者&#xff1a;Commas 签名&#xff1a;(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释&#xff1a;如果您觉得有所帮助&#xff0c;帮忙点个赞&#xff0c;也可以关注我&#xff0c;我们一起成长&#xff1b;如果有不对的地方&#xf…

解析前端开发中同源策略与配置代理

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、pandas是什么&#xff1f;二、使用步骤 1.引入库2.读入数据总结 前言 在前端开发中&#xff0c;跨域请求是一个常见的问题。同源策略限制了浏览器中一个页面…

win10系统下WPS工具显示灰色全部用不了,提示登录

如果你在写文档或使用excel时发现导航栏的工具全部使用不了&#xff0c;弹出是需要您登录&#xff0c;可以通过以下操作不用登录。 按照 1&#xff08;搜索框&#xff09;—> 2&#xff08;应用&#xff09;—> 3&#xff08;WPS Office&#xff09;点鼠标左键—> 4&a…

使用LeanCloud平台的即时通讯

LeanCloud 是领先的 Serverless 云服务&#xff0c;为产品开发提供强有力的后端支持&#xff0c;旨在帮助开发者降低研发、运营维护等阶段投入的精力和成本。 LeanCloud 整合了各项服务&#xff0c;让开发者能够聚焦在核心业务上&#xff0c;为客户创造更多价值。 *即时通讯 …

PromptIR论文阅读笔记

MZUAI和IIAI在NIPS2023上的一篇论文&#xff0c;用prompt来编码degradation&#xff0c;然后用来guide restoration network&#xff0c;使得模型能够泛化到不同degradation types and levels&#xff0c;也就是说是一个模型一次训练能够应对多种degradation的unified model。文…

apache大数据各组件部署搭建(超级详细)

apache大数据数仓各组件部署搭建 第一章 环境准备 1. 机器规划 准备3台服务器用于集群部署,系统建议CentOS7+,2核8G内存 172.19.195.228 hadoop101 172.19.195.229 hadoop102 172.19.195.230 hadoop103 [root@hadoop101 ~]# cat /etc/redhat-release CentOS Linux rele…

Azure DevOps Server 2022.2(升级过程)

1. 概述 2. 前期准备3. 升级过程4. 验证成果 1. 概述 本月微软公司发布了Azure DevOps Server 2022的第二个升级包Update 2 https://learn.microsoft.com/en-us/azure/devops/server/release-notes/azuredevops2022u2。 自2024年3月12日发布Azure DevOps Server 2022 Update 1(…

Linux综合实践(Ubuntu)

目录 一、配置任务 1.1 配置该服务器的软件源为中科大软件源 1.2 安装相关软件openssh-server和vim 1.3 设置双网卡&#xff0c;网卡1为NAT模式&#xff0c;网卡2为桥接模式(桥接模式下&#xff0c;使用静态ip&#xff0c;该网卡数据跟实验室主机网络设置相似&#xff0c;除…

Jvm(一)之栈、堆、方法区

前言-与正文无关 生活远不止眼前的苦劳与奔波&#xff0c;它还充满了无数值得我们去体验和珍惜的美好事物。在这个快节奏的世界中&#xff0c;我们往往容易陷入工作的漩涡&#xff0c;忘记了停下脚步&#xff0c;感受周围的世界。让我们一起提醒自己&#xff0c;要适时放慢脚步…