【设计模式-6】建造者模式的实现与框架中的应用

 建造者模式又被成为生成器模式,是一种使用频率比较低,相对复杂的创建型模式,在很多源码框架中可以看到建造者的使用场景,稍后我们会在本文末尾展示几个框架的使用案例。
 建造者模式所构造的对象通常是比较复杂而且庞大的,也是按照既定的构建顺序将对象中的各个属性组装起来。与工厂模式不同的是,建造者模式主要目的是把繁琐的构建过程从产品类和工厂类中抽离出来,进一步解耦,最终实现用一套标准的制造工序制造出不同的产品。

1. 定义

建造者模式 的官方定义是:将一个复杂对象的构建和表示分离,使得同样的构建过程可以创建不同的表示。建造者模式一步一步地创建一个复杂的对象,它允许用户通过指定复杂对象的类型和内容就可以构建它们,用户不需要知道内部的构造细节。

 官方的表达还是挺难理解的,总体意思就是,构建过程固定,构建使用的每个组件都可以通过继承或者实现 呈现出多态的特性,这依赖于抽象建造者接口和具体的建造者实现类两个组件来表示。

 在建造者模式中,通常包含4个角色:

  • 产品:复杂的产品类,建造者的最终产物,构建过程相对复杂,需要很多组件组装而成。
  • 抽象建造者:建造者接口,包含两个方便,一个是构成产品的各个属性,一个是各个属性的构建步骤方法。
  • 建造者实现:建造者接口的实现类,可以有多个,对应不同的组件实现细节,但是不会包含构建的顺序逻辑。
  • 指导者:客户端依赖的创建产品的指导者类,指导者类会通过建造者接口引入具体的建造者实现,并且包含了具体的构建顺序。

2. 代码实现

 建造者的实现逻辑想对复杂,下面展示一个酒水制造的代码案例,某个酒厂生产啤酒和红酒两类产品,对应上述的四个角色,代码实现如下:

  • 1. 产品
// 产品对象
public class Wine {

    // 用list封装制作工艺,表示制作的顺序是固定的
    private List<String> list = new ArrayList();
    // 1.准备原材料
    public void setPrepareMaterial(String prepareMaterial) {
        list.add(prepareMaterial);
    }
    // 2.制作工艺
    public void setCraftsmanShip(String craftsmanShip) {
        list.add(craftsmanShip);
    }
    // 3.出厂包装
    public void setFactoryPackage(String factoryPackage) {
        list.add(factoryPackage);
    }

    @Override
    public String toString() {
        return "Wine{" + "list=" + list + '}';
    }
}
  • 2. 抽象建造者
// 抽象的建造者
public abstract class AbstractBuilder {
    // 自定义产品
    Wine wine = new Wine();

    // 设置原材料
    public abstract void setPrepareMaterial();
    // 设置制作工艺
    public abstract void setCraftsmanShip();
    // 设置出厂包装
    public abstract void setFactoryPackage();

    // 获取产品 - 注意这里是个钩子方法,子类可以不实现
    public Wine getProduct() {
        return wine;
    }
}
  • 3. 建造者实现
// 啤酒建造者-具体的子类实现
public class BeerBuilder extends AbstractBuilder {

    @Override
    public void setPrepareMaterial() {
        wine.setPrepareMaterial("1.准备原材料:小麦 + 豆子");
    }

    @Override
    public void setCraftsmanShip() {
        wine.setCraftsmanShip("2.制作工艺:发酵 + 蒸馏");
    }

    @Override
    public void setFactoryPackage() {
        wine.setFactoryPackage("3.出厂包装:易拉罐 + 纸箱");
    }
}

// 葡萄酒制造者 - 具体的子类实现
public class GrapeBuilder extends AbstractBuilder{

    @Override
    public void setPrepareMaterial() {
        wine.setPrepareMaterial("1.准备原材料:葡萄 + 酵母");
    }

    @Override
    public void setCraftsmanShip() {
        wine.setCraftsmanShip("2.制作工艺:发酵 + 地下贮存");
    }

    @Override
    public void setFactoryPackage() {
        wine.setFactoryPackage("3.出厂包装:玻璃瓶 + 木箱");
    }
}
  • 4. 指导者
// 指挥者 - 构造产品对象
public class Director {

    private AbstractBuilder abstractBuilder;

    public Director(AbstractBuilder abstractBuilder) {
        this.abstractBuilder = abstractBuilder;
    }

    // 这里固定制造的工序,按照固定步骤执行
    public Wine createProduct() {
        // 第一步:设置原材料
        abstractBuilder.setPrepareMaterial();
        // 第二步:设置制作工艺
        abstractBuilder.setCraftsmanShip();
        // 第三步:设置出厂包装
        abstractBuilder.setFactoryPackage();
        // 第四步,返回产品
        return abstractBuilder.getProduct();
    }
}
  • 客户端
// 客户端
public class Client {
    public static void main(String[] args) {
        // 啤酒制造
        Director beerDirector = new Director(new BeerBuilder());
        Wine beer = beerDirector.createProduct();
        System.out.println(beer);
        
        // 红酒制造
        Director grapeDirector = new Director(new GrapeBuilder());
        Wine grape = grapeDirector.createProduct();
        System.out.println(grape);
    }
}

3. UML类图

 对照上述的代码Demo,我们来把这个案例的类图画出来,对应如下:
在这里插入图片描述

4. 简化

 因为建造者是属于相对复杂的一种模式,在实际的应用当中有很多种简化的写法,比如可以忽略指导者类、建造者接口等等。下面是一种实现方式,这种实现方式在很多源码中可以看到具体实现。

 通常在产品类的构造函数参数超过4个,而且这些参数中有一些是必填项,考虑使用这种建造者模式。现在我们来构建一个电脑的产品对象。

// 电脑产品对象
public class Computer {
    private String cpu; // 必选
    private String ram; // 必选
    private String keyboard; // 可选
    private String mouse; // 可选
    private String display; // 可选
}

 这种bean类的属性设置有两种方式,一个是构造函数入参,一个是通过set()方法入参,这两种方式都有些问题。构造函数入参,当参数较多的时候,类型相同的情况下,属性的顺序容易混乱;第二个中set()方式,一个对象会支持在很多模块中设置,因为类中的属性是可以分布设置的,所以容易出现属性状态变化造成错误。

 下面是一种简易的建造者实现方式(在实际场景中可能属性的构建过程很复杂):

  • 建造者模式
// 电脑产品对象
public class Computer {
    private final String cpu; // 必选
    private final String ram; // 必选
    private final String keyboard; // 可选
    private final String mouse; // 可选
    private final String display; // 可选

    private Computer(Computer.Builder builder) {
        this.cpu = builder.cpu;
        this.ram = builder.ram;
        this.keyboard = builder.keyboard;
        this.mouse = builder.mouse;
        this.display = builder.display;
    }

    public static Computer.Builder builder(String cpu, String ram) {
        return new Computer.Builder(cpu, ram);
    }

    public static class Builder {
        private String cpu; // 必选
        private String ram; // 必选
        private String keyboard; // 可选
        private String mouse; // 可选
        private String display; // 可选

        Builder(String cpu, String ram) {
            this.cpu = cpu;
            this.ram = ram;
        }

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

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

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

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

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

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

    @Override
    public String toString() {
        return "Computer{" + "cpu='" + cpu + '\'' + ", ram='" + ram + '\'' + ", keyboard='" + keyboard + '\''
            + ", mouse='" + mouse + '\'' + ", display='" + display + '\'' + '}';
    }
}
  • 客户端实现
// 客户端
public class Client {
    public static void main(String[] args) {
        Computer computer = Computer.builder("cpu", "ram")
            .cpu("cpu-1").mouse("mouse").display("display").build();
        System.out.println(computer);
    }
}

5. 总结

 建造者模式的核心在于如何一步一步地构建一个包含多个组成部件的完整对象,使用相同的构建过程可以构建不同的产品。在软件开发中,如果需要创建复杂对象,并且系统系统具备良好的灵活性和扩展性,可以考虑使用建造者模式。

  • 首先建造者模式使得客户端和产品创建的过程解耦,客户端可以不知道产品创建的内部细节;
  • 构建过程固定的情况下,构建的细节可以多变性,这就可以很方便的增加不同的构造实现,符合开闭原则;
  • 将每个属性的构建过程分解在不同的方法中,精细化管理,高内聚才能低耦合,符合单一职责。

6. 框架中的应用

  • StringBuilder和StringBuffer
  • SqlSessionBuilder
  • Lombok的@Builder注解

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

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

相关文章

PaddleSeg的训练与测试推理全流程(超级详细)

LeNet模型量化 参考文档一.下载项目地址&#xff1a;https://gitee.com/paddlepaddle/PaddleSeg/tree/release%2F2.5/特别注意下载版本&#xff1a; 二.paddlepaddle-gpu安装1.环境安装参考文档&#xff1a;https://gitee.com/paddlepaddle/PaddleSeg/blob/release/2.8/docs/in…

x-cmd pkg | pypinyi - 汉字拼音转换工具

目录 简介首次用户功能特点相关工具进一步探索 简介 pypinyin 是一个汉字拼音转换工具&#xff0c;支持多种词库&#xff0c;多种输出格式&#xff0c;支持自定义词组拼音库或单字拼音库。 首次用户 使用 x env use pypinyin 即可自动下载并使用 在终端运行 eval "$(cur…

计算机中vcruntime140.dll丢失如何修复,马上教会你

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“VCRuntime140.dll丢失”。VCRuntime140.dll是Visual C Redistributable Packages的一部分&#xff0c;它提供了许多常用的运行时函数。当这个文件丢失或损坏时&#xff0c;可能会导致某些应…

Python如何对csv文件进行操作

csv是Comma-Separated Values的缩写&#xff0c;是用文本文件形式储存的表格数据&#xff0c;比如如下的表格&#xff1a; 就可以存储为csv文件&#xff0c;文件内容是&#xff1a; No.,Name,Age,Score1,mayi,18,99 2,jack,21,89 3,tom,25,95 4,rain,19,80 假设上述csv文件保存…

基于STM32F103C8T6单片机的DHT11温湿度传感器与OLED显示屏实时动态数据监测系统设计

标题&#xff1a; 摘要&#xff1a; 本文提出了一种利用STM32F103C8T6微控制器&#xff0c;结合DHT11数字温湿度传感器和OLED显示屏实现环境温湿度实时、直观显示的方法。该系统通过低功耗且精确的DHT11传感器获取环境温湿度信息&#xff0c;并借助于STM32F103C8T6强大的处理能…

大模型学习之书生·浦语大模型3——基于InternLM和LangChain搭建知识库

基于InternLM和LangChain搭建知识库 1 大模型开发范式 LLM的局限性 知识受限&#xff1a;最新知识无法实时获取专业能力有限&#xff1a;有广度无深度定制化成本高&#xff1a;训练成本高 RAG VS Finetune RAG&#xff1a; 无需重新训练组织外挂加入知识容易受基座模型的影响…

软件测试|解读Python的requirements.txt文件:管理项目依赖的完整指南

简介 在Python项目中&#xff0c;管理依赖库是必不可少的。requirements.txt文件是一种常用的方式&#xff0c;用于列出项目所需的所有依赖库及其版本。本文将详细介绍requirements.txt的用法&#xff0c;帮助你更好地管理项目的依赖。 使用步骤 创建requirements.txt文件&am…

Linux之Iptables简易应用

文档形成时期&#xff1a;2009-2024年 和iptables打交道有15年了&#xff0c;经过无数实践后&#xff0c;形成一个简易应用文档。 文档主题是简易应用&#xff0c;所以其原理不详述了。 因软件世界之复杂和个人能力之限&#xff0c;难免疏漏和错误&#xff0c;欢迎指正。 文章目…

强化app广告变现用户隐私合规,移动广告变现合规技巧

移动广告技术的发展帮助开发者极大提升了广告变现效率&#xff0c;APP作为用户个人信息处理的重要载体&#xff0c;自从《个人信息保护法》颁布以来&#xff0c;个人信息的使用已经成为监管重点&#xff0c;开发者强化合规意识&#xff0c;让广告变现业务“细水长流”&#xff…

无公网ip如何随时随地远程查看本地群晖NAS存储的文件资源

文章目录 前言本教程解决的问题是&#xff1a;按照本教程方法操作后&#xff0c;达到的效果是前排提醒&#xff1a; 1. 搭建群晖虚拟机1.1 下载黑群晖文件vmvare虚拟机安装包1.2 安装VMware虚拟机&#xff1a;1.3 解压黑群晖虚拟机文件1.4 虚拟机初始化1.5 没有搜索到黑群晖的解…

Cylinder3D论文阅读

Cylindrical and Asymmetrical 3D Convolution Networks for LiDAR Segmentation&#xff08;2020年论文&#xff09; 作者&#xff1a;香港中文大学 论文链接&#xff1a;https://arxiv.org/pdf/2011.10033.pdf 代码链接&#xff1a;https://github.com/xinge008/Cylinder3D …

BP神经网络(公式推导+举例应用)

文章目录 引言M-P神经元模型激活函数多层前馈神经网络误差逆传播算法缓解过拟合化结论实验分析 引言 人工神经网络&#xff08;Artificial Neural Networks&#xff0c;ANNs&#xff09;作为一种模拟生物神经系统的计算模型&#xff0c;在模式识别、数据挖掘、图像处理等领域取…

【工具栏】RestfulTool 插件的使用(接口开发规范)

目录 1. 安装 2.使用 1. 安装 2.使用 点开控制层&#xff0c;该控制层写了什么接口一目了然 跳转对应的接口&#xff0c;查看代码

基于JAVA+ssm智能旅游线路规划系统设计与实现【附源码】

基于JAVAssm智能旅游线路规划系统设计与实现【附源码】 &#x1f345; 作者主页 央顺技术团队 &#x1f345; 欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; &#x1f345; 文末获取源码联系方式 &#x1f4dd; 项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql…

【Jmeter之get请求传递的值为JSON体实践】

Jmeter之get请求传递的值为JSON体实践 get请求的常见传参方式 1、在URL地址后面拼接&#xff0c;有多个key和value时&#xff0c;用&链接 2、在Parameters里面加上key和value 第一次遇到value的值不是字符串也不是整型&#xff0c;我尝试把json放到value里面&#xff0…

迅为RK3588开发板编译 Buildroot单独编译图形化界面三

第三步&#xff1a;编译 Recovery 首先在 linux 源码目录下输入以下命令进入编译的 UI 界面&#xff0c;进入之后如下所示&#xff1a; ./build.sh 然后将光标移动到第四个 recovery&#xff0c;点击回车即可开始 recovery 的编译&#xff0c;编译过程如下所示&#xff1a; 编…

OpenAI推出GPT商店和ChatGPT Team服务

&#x1f989; AI新闻 &#x1f680; OpenAI推出GPT商店和ChatGPT Team服务 摘要&#xff1a;OpenAI正式推出了其GPT商店和ChatGPT Team服务。用户已经创建了超过300万个ChatGPT自定义版本&#xff0c;并分享给其他人使用。GPT商店集结了用户为各种任务创建的定制化ChatGPT&a…

重建传播网络并识别隐藏来源

1.摘要 我们从数据中揭示复杂网络结构和动态的能力&#xff0c;对于理解和控制复杂系统中的集体动态至关重要。尽管在这一领域已有近期进展&#xff0c;但如何从有限的时间序列中重建具有随机动态过程的网络仍然是一个突出问题。在这里&#xff0c;我们开发了一个基于压缩感知的…

微信小程序开发学习笔记《7》全局配置以及小程序窗口

微信小程序开发学习笔记《7》全局配置以及小程序窗口 博主正在学习微信小程序开发&#xff0c;希望记录自己学习过程同时与广大网友共同学习讨论。全局配置官方文档 一、全局配置文件及常用的配置项 小程序根目录下的app.json 文件是小程序的全局配置文件。 常用的配置项如…

企业培训系统源码:构建智能、可扩展的学习平台

企业培训系统在现代企业中扮演着至关重要的角色。本文将通过深度解析企业培训系统的源码&#xff0c;介绍如何构建一个智能、可扩展的学习平台&#xff0c;涉及关键技术和代码实例。 1. 技术栈选择与项目初始化 在构建企业培训系统之前&#xff0c;选择适当的技术栈是至关重…