创建型模式-建造者模式

news2024/11/27 2:47:21

建造者模式

概述

将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示
在这里插入图片描述

这个模式适用于:某个对象的构建过程复杂的情况

  • 将部件的构造与装配分离,由 Builder 负责构造,Director 进行装配,实现了构建和装配的解耦。
  • 不同的构建器,相同的装配,可以做出不同的对象。
  • 相同的构建器,不同的装配顺序,也可以做出不同的对象。
  • 用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节。

结构

建造者(Builder)模式包含如下角色:

  • 抽象建造者类 Builder:这个接口规定要实现复杂对象的那些部分的创建,并不涉及具体的部件对象的创建。
  • 具体建造者类 Concrete Builder:实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。在构造过程完成后,提供产品的实例。
  • 产品类 Product:要创建的复杂对象。
  • 指挥者类 Director:调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。

在这里插入图片描述

  • 指挥者类用来指挥建造顺序,建造者只负责造部件。

  • 抽象工厂生产出来的是某个系列的各种单品,构造者模式生产多种零件组装成一个单品。

  • 抽象工厂用来对对象的生成进行解耦,建造者模式用来应对复杂对象的生成。

案例

创建共享单车

生产自行车是一个复杂的过程,它包含了车架,车座等组件的生产:

  • 车架有碳纤维,铝合金等材质。
  • 车座有橡胶,真皮等材质。

对于自行车的生产就可以使用建造者模式,类图如下:

在这里插入图片描述

  • Bike 是产品,包含车架,车座等组件。
  • Builder 是抽象建造者。
  • MobikeBuilder 和 OfoBuilder 是具体的建造者。
  • Director 是指挥者。

代码

产品对象:自行车

package com.vmware.builder;

/**
 * @apiNote 产品 包含车座车架
 */
public class Bike {
    private String frame;
    private String seat;

    @Override
    public String toString() {
        return "Bike{" +
                "frame='" + frame + '\'' +
                ", seat='" + seat + '\'' +
                '}';
    }

    public String getFrame() {
        return frame;
    }

    public void setFrame(String frame) {
        this.frame = frame;
    }

    public String getSeat() {
        return seat;
    }

    public void setSeat(String seat) {
        this.seat = seat;
    }
}

抽象构建者:Builder

public abstract class Builder {
    protected Bike bike = new Bike();

    abstract public void buildFrame();

    abstract public void buildSeat();

    abstract public Bike createBike();
}

具体构建者类:摩拜单车、Ofo单车

/**
 * @apiNote 具体构建者
 */
public class MobikeBuilder extends Builder {
    @Override
    public void buildFrame() {
        bike.setFrame("摩拜车架");
    }

    @Override
    public void buildSeat() {
        bike.setSeat("摩拜车坐");
    }

    @Override
    public Bike createBike() {
        return bike;
    }
}
/**
 * @apiNote 具体构建者
 */
public class OfoBuilder extends Builder {
    @Override
    public void buildFrame() {
        bike.setFrame("Ofo车架");
    }

    @Override
    public void buildSeat() {
        bike.setSeat("Ofo车坐");
    }

    @Override
    public Bike createBike() {
        return bike;
    }
}

指挥者类

/**
 * @apiNote 指挥者
 */
public class Director {
    private final Builder builder;

    public Director(Builder builder) {
        this.builder = builder;
    }

    public Bike construct(){
        builder.buildFrame();
        builder.buildSeat();
        return builder.createBike();
    }
}

测试类

public class BuilderTest {
    public static void main(String[] args) {
        //构建Ofo
        Director director = new Director(new OfoBuilder());
        Bike bike = director.construct();
        System.out.println(bike);
        //构建Mobike
        Director director1 = new Director(new MobikeBuilder());
        Bike bike1 = director1.construct();
        System.out.println(bike1);
    }
}

输出

Bike{frame='Ofo车架', seat='Ofo车坐'}
Bike{frame='摩拜车架', seat='摩拜车坐'}

上面示例是 Builder 模式的常规用法,指挥者类 Director 在建造者模式中具有很重要的作用,它用于指导具体构建者如何构建产品,控制调用先后次序,并向调用者返回完整的产品类

但是有些情况下可以简化系统结构,可以把指挥者类和抽象建造者进行结合:

抽象构建者

/**
 * @apiNote 抽象构建者
 */
public abstract class Builder {
    protected Bike bike = new Bike();

    abstract public void buildFrame();

    abstract public void buildSeat();

    abstract public Bike createBike();

    public Bike construct() {
        buildFrame();
        buildSeat();
        return createBike();
    }
}

这样做确实简化了系统结构,但同时也加重了抽象建造者类的职责,不符合单一职责原则。如果 construct() 过于复杂,建议还是封装到 Director 中。

优缺点

优点:

  • 建造者模式的封装性很好。使用建造者模式可以有效的封装变化,在使用建造者模式的场景中,一般产品类和建造者类是比较稳定的,因此,将主要的业务逻辑封装在指挥者类中对整体而言可以取得比较好的稳定性。

  • 在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。

  • 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。

  • 建造者模式很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成,基本上不用修改之前已经测试通过的代码,因此也就不会对原有功能引入风险,符合开闭原则。

缺点:

  • 使用范围有一定的限制。

  • 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,则不适合使用建造者模式。

使用场景

建造者模式创建的是复杂对象,其产品的各个部分经常面临着剧烈变化,但将它们组合在一起的算法却相对稳定,所以它通常在以下场合使用。

  • 创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但部件间的建造顺序是稳定的。
  • 创建复杂对象的算法独立于该对象的组成部分以及它们的装配方式,即产品的构建过程和最终的表示是独立的

拓展-构建对象

建造者模式除了上面的用途外,在开发中还有一个常用的使用方式,就是当一个类构造器需要传入很多参数时,如果创建这个类的实例,代码可读性会非常差,而且很容易引入错误,此时就可以利用建造者模式进行重构。

重构前

public class Phone {
    private String cpu;
    private String screen;
    private String memory;
    private String mainboard;

    public Phone(String cpu, String screen, String memory, String mainboard) {
        this.cpu = cpu;
        this.screen = screen;
        this.memory = memory;
        this.mainboard = mainboard;
    }

    public String getCpu() {
        return cpu;
    }

    public void setCpu(String cpu) {
        this.cpu = cpu;
    }

    public String getScreen() {
        return screen;
    }

    public void setScreen(String screen) {
        this.screen = screen;
    }

    public String getMemory() {
        return memory;
    }

    public void setMemory(String memory) {
        this.memory = memory;
    }

    public String getMainboard() {
        return mainboard;
    }

    public void setMainboard(String mainboard) {
        this.mainboard = mainboard;
    }

    @Override
    public String toString() {
        return "Phone{" +
                "cpu='" + cpu + '\'' +
                ", screen='" + screen + '\'' +
                ", memory='" + memory + '\'' +
                ", mainboard='" + mainboard + '\'' +
                '}';
    }
}

在客户端中构建对象:传递了 4 个参数,如果参数更多呢?代码的可读性及使用的成本就是比较高

public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone("intel","三星屏幕","金士顿","华硕");
        System.out.println(phone);
    }
}

重构后

使用构建者模式重构代码

package com.vmware.builder.demo;

public class Phone {
    private String cpu;
    private String screen;
    private String memory;
    private String mainBoard;

    @Override
    public String toString() {
        return "Phone{" +
                "cpu='" + cpu + '\'' +
                ", screen='" + screen + '\'' +
                ", memory='" + memory + '\'' +
                ", mainBoard='" + mainBoard + '\'' +
                '}';
    }

    private Phone(PhoneBuilder builder) {
        this.cpu = builder.cpu;
        this.screen = builder.screen;
        this.memory = builder.memory;
        this.mainBoard = builder.mainBoard;
    }

    public static final class PhoneBuilder {
        private String cpu;
        private String screen;
        private String memory;
        private String mainBoard;

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

        public PhoneBuilder screen(String screen) {
            this.screen = screen;
            return this;
        }

        public PhoneBuilder memory(String memory) {
            this.memory = memory;
            return this;
        }

        public PhoneBuilder mainBoard(String mainBoard) {
            this.mainBoard = mainBoard;
            return this;
        }

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

在客户端中构建对象:使用起来更灵活方便,某种程度上也可以提高开发效率

public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone.PhoneBuilder()
                .cpu("英特尔")
                .screen("京东方")
                .mainBoard("华硕")
                .memory("金士顿")
                .build();
        System.out.println(phone);
    }
}

🔖 在 Lombok 中只需要使用 @Builder 即可使用构建者模式来构建对象。

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

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

相关文章

LeetCode——新手村

目录 前言 一、一维数组的动态和 1、题目 2、代码 二、将数字变成 0 的操作次数 1、题目 2、代码 三、最富有客户的资产总量 1、题目 2、代码 四、Fizz Buzz 1、题目 2、代码 五、链表的中间结点 1、题目 2、代码 六、赎金信 1、题目 2、代码 前言 注册了一个LeetCode的…

10、Mysql常见面试题

Mysql常见面试题 文章目录 Mysql常见面试题一 Mysql索引001 Mysql如何实现的索引机制?002 InnoDB索引与MyISAM索引实现的区别是什么?003 一个表中如果没有创建索引,那么还会创建B树吗? 004 说一下B树索引实现原理(数据…

毕业5年的同学突然告诉我,他已经是年薪30W的自动化测试工程师....

作为一名程序员,都会对自己未来的职业发展而焦虑。一方面是因为IT作为知识密集型的行业,知识体系复杂且知识更新速度非常快,“一日不学就会落后”。 另外一方面,IT又是劳动密集型的行业,不仅业人员多,而且个…

8个你可能不知道的令人震惊的 HTML 技巧

大厂面试题分享 面试题库 前后端面试题库 (面试必备) 推荐:★★★★★ 地址:前端面试题库 web前端面试题库 VS java后端面试题库大全 1. 捕获属性打开你的设备摄像头 正如 input 标记具有 email、 text 和 password 属性一样&…

Unity音量滑块沿弧形移动

一、音量滑块的移动 1、滑块在滑动的时候,其运动轨迹沿着大圆的弧边展开 2、滑块不能无限滑动,而是两端各有一个挡块,移动到挡块位置,则不能往下移动,但可以折回 3、鼠标悬停滑块时,给出音量值和操作提示 …

JMeter 获取登录接口的token

1、登录接口为POST请求方式,添加请求登录接口的消息体数据 添加HTTP信息头管理器,配置content-type值为application/json 2、给登录接口“添加监听器-查看结果树”和“后置处理器-正则表达式处理器” 先运行一次登录接口,通过查看结果树返回内…

C++三大特性—继承 “访问控制”

本文主要阐述关于C继承中基类与派生类之间的访问关系 继承方式与访问方式 继承定义格式: 派生类可以继承定义在基类的成员,但是派生类的成员函数不一定有权访问从基类继承来的成员    访问限定符的作用:控制派生类从基类继承而来的成员是否…

Matlab——逻辑回归(原理、代码)

对于一个机器学习方法,通常由模型、策略和算法3个要素构成。 模型是假设空间的形式,如是线性函数还是条件概率;策略是判断模型好坏的数学表达式,将学习问题转化为优化问题,一般策略对应一个代价函数(Cost F…

SQL优化(2):主键优化

在上一小节,我们提到,主键顺序插入的性能是要高于乱序插入的。 这一小节,就来介绍一下具体的 原因,然后再分析一下主键又该如何设计。 1 数据组织方式 在InnoDB存储引擎中,表数据都是根据主键顺序组织存放的&#xf…

数据结构——求二叉树的属性

数据结构——求二叉树的属性 一、对称性101. 对称二叉树1.递归2.迭代3.同类题: 二、深度104. 二叉树的最大深度1.递归1)后序1)前序 2.迭代(层序) 559. N 叉树的最大深度1.递归(深度优先)2.迭代&…

MATLAB实现OCR识别数字和字符

OCR也叫做光学字符识别,是计算机视觉研究领域的分支之一。它是利用光学技术和计算机技术把印在或写在纸上的文字读取出来,并转换成一种计算机能够接受、人又可以理解的格式。 MATLAB实现OCR识别数字和字符,涉及灰度转换、中值滤波、二值化处…

【教学类-34-05】拼图(彩色图片+凹凸拼)3*4格子(中班主题《个别化拼图》偏美术)

图片展示: 背景需求: 最近班级孩子得了传染病,来了2位孩子。 我觉得:人少的话,孩子们就有充足的时间去拼那些带有凹凸槽的自制彩色图形拼图。 难点: 1、从直线剪切(方形拼图)转…

[JAVA编] 一编让你搞定多态

目录 1. 多态概念 2. 多态的体现和实现条件 3. 重写 4. 引用类型转换 4.1向上转型 4.2向下转型 5. 多态的好处 1.多态的概念 什么是多态? 多态是继封装, 继承之后, 面向对象的三大特性 在生活中,比如跑的动作,猫,狗和大象,跑起来都不一样.再比如飞…

【cmd命令】MySQL服务器无法启动

winR 输入services.msc 我发现我电脑上有两个mysql名,上面一个(MySQL)处于停止运行状态 下面一个(MySQL)处于运行状态 如果要使用上面一个的服务器,就要把下面一个的服务器关闭,然后启动上面的服务器…

elastic-job 搭建——应用于企业级项目

1. 📂 技术方案 方案介绍 ElasticJob 是面向互联网生态和海量任务的分布式调度解决方案。 它通过弹性调度、资源管控、以及作业治理的功能,打造一个适用于互联网场景的分布式调度解决方案,并通过开放的架构设计,提供多元化的作业…

你知道渲染农场是什么原理吗?它是如何工作的?

我们知道,仅靠一台计算机几乎是不能达到专业渲染集群的处理能力的。所以现在, 允许将很多台计算机或是处理器进行连接,再将连接后的机器作为一个总平台来处理不同的渲染需求,这样的设置,就被称之为渲染农场。 渲染农…

年轻人“赶烤”淄博,文旅业如何借势?

​(图片来源于网络,侵删) 文 | 螳螂观察 作者 | 易不二 从“更适合中国宝宝体质的TACO”在社交媒体爆火,到全国人民为之“赶烤”,淄博凭借独树一帜的烧烤文化,已经站上了文旅业回暖的潮头。 今年五一假期…

FPGA目前就业形势咋样?来听听业内工程师的看法

看到网上有一个问题很火:2023了,FPGA目前就业形势咋样?很多同学也对这个方向比较感兴趣,下面就来一起了解一下吧。 FPGA岗位有哪些? 从芯片设计流程来看,FPGA岗位可以分四类 产品开发期:FPGA系统架构师 …

基于FFmpeg倒放功能的实现-----命令行和API调用实现方法

来源:微信公众号「编程学习基地」 文章目录 FFmpeg API调用reverse滤镜实现视频倒放ffmpeg命令行实现方法FFmpeg 过滤器 调用API实现方法完整代码贴上运行FFmpeg API调用reverse滤镜实现视频倒放 ffmpeg命令行实现方法 ffmpeg -i bigbuckbunny_480x272.h265 -filter_comple…

vue页面内嵌iframe使用postMessage进行数据交互(postMessage跨域通信)

什么是postMessage postMessage是html5引入的API,它允许来自不同源的脚本采用异步方式进行有效的通信,可以实现跨文本文档,多窗口,跨域消息传递.多用于窗口间数据通信,这也使它成为跨域通信的一种有效的解决方案. vue父页面(嵌入iframe的页面) 在vue中…