【设计模式】创建者模式·建造者模式

news2024/11/25 13:01:35

学习汇总入口【23种设计模式】学习汇总(数万字讲解+体系思维导图)
写作不易,如果您觉得写的不错,欢迎给博主来一波点赞、收藏~让博主更有动力吧!

一.概述

将一个复杂对象的构建与表示分离,使得同样的构建过程可以创建不同的表示。

在这里插入图片描述

  • 实现了构建和装配的解耦。不同的构建器,相同的装配,也可以做出不同的对象;相同的构建器,不同的装配顺序也可以做出不同的对象。也就是实现了构建算法、装配算法的解耦,实现了更好的复用。
  • 分离了部件的构造(由Builder来负责)和装配(由Director负责),一步一步创建一个复杂的对象。用户只需要指定复杂对象的类型就可以得到该对象,而无须知道其内部的具体构造细节。
  • 建造者(Builder)模式包含如下角色:

    • 抽象建造者类(Builder):这个接口规定要实现复杂对象的那些部分的创建,并不涉及具体的部件对象的创建。

    • 具体建造者类(ConcreteBuilder):实现 Builder 接口,完成复杂产品的各个部件的具体创建方法。在构造过程完成后,提供产品的实例。

    • 产品类(Product):要创建的复杂对象。

    • 指挥者类(Director):调用具体建造者来创建复杂对象的各个部分,在指导者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。

    • 类图如下:
      在这里插入图片描述

二.使用

(1) 示例

创建共享单车

生产自行车是一个复杂的过程,它包含了车架,车座等组件的生产。而车架又有碳纤维,铝合金等材质的,车座有橡胶,真皮等材质。对于自行车的生产就可以使用建造者模式。

这里Bike是产品,包含车架,车座等组件;Builder是抽象建造者,MobikeBuilder和OfoBuilder是具体的建造者;Director是指挥者。类图如下:
在这里插入图片描述
具体的代码如下:

//自行车类
public class Bike {
    private String frame;
    private String 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 mBike = new Bike();

    public abstract void buildFrame();
    public abstract void buildSeat();
    public abstract Bike createBike();
}

//摩拜单车Builder类
public class MobikeBuilder extends Builder {

    @Override
    public void buildFrame() {
        mBike.setFrame("铝合金车架");
    }

    @Override
    public void buildSeat() {
        mBike.setSeat("真皮车座");
    }

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

//ofo单车Builder类
public class OfoBuilder extends Builder {

    @Override
    public void buildFrame() {
        mBike.setFrame("碳纤维车架");
    }

    @Override
    public void buildSeat() {
        mBike.setSeat("橡胶车座");
    }

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

//指挥者类
public class Director {
    private Builder mBuilder;

    public Director(Builder builder) {
        mBuilder = builder;
    }

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

//测试类
public class Client {
    public static void main(String[] args) {
        showBike(new OfoBuilder());
        showBike(new MobikeBuilder());
    }
    private static void showBike(Builder builder) {
        Director director = new Director(builder);
        Bike bike = director.construct();
        System.out.println(bike.getFrame());
        System.out.println(bike.getSeat());
    }
}
  • 说明:上面示例是 Builder模式的常规用法,指挥者类 Director 在建造者模式中具有很重要的作用,它用于指导具体构建者如何构建产品,控制调用先后次序,并向调用者返回完整的产品类,
  • 简化形式:有些情况下可以简化系统结构,将指挥者类和抽象建造者进行结合
// 抽象 builder 类
public abstract class Builder {

    protected Bike mBike = new Bike();

    public abstract void buildFrame();
    public abstract void buildSeat();
    public abstract Bike createBike();
    
    public Bike construct() {
        this.buildFrame();
        this.BuildSeat();
        return this.createBike();
    }
}
  • 说明:简化了系统结构,但同时也加重了抽象建造者类的职责,不太符合单一职责原则,如果construct() 过于复杂,建议还是封装到 Director 中。

(2) 优缺点

优点:

  • 使用建造者模式可以有效的封装变化,在使用建造者模式的场景中,一般产品类和建造者类是比较稳定的,因此,将主要的业务逻辑封装在指挥者类中对整体而言可以取得比较好的稳定性。
  • 在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。
  • 可以更加精细地控制产品的创建过程 。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。
  • 建造者模式很容易进行扩展。如果有新的需求,通过实现一个新的建造者类就可以完成,基本上不用修改之前已经测试通过的代码,因此也就不会对原有功能引入风险。符合开闭原则。

缺点:

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

(3) 使用场景

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

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

(4) 链式构造

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

重构前代码如下:

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 + '\'' +
                '}';
    }
}

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

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

重构后代码:

public class Phone {

    private String cpu;
    private String screen;
    private String memory;
    private String mainboard;

    private Phone(Builder builder) {
        cpu = builder.cpu;
        screen = builder.screen;
        memory = builder.memory;
        mainboard = builder.mainboard;
    }

    public static final class Builder {
        private String cpu;
        private String screen;
        private String memory;
        private String mainboard;

        public Builder() {}

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

public class Client {
    public static void main(String[] args) {
        Phone phone = new Phone.Builder()
                .cpu("intel")
                .mainboard("华硕")
                .memory("金士顿")
                .screen("三星")
                .build();
        System.out.println(phone);
    }
}

重构后的代码在使用起来更方便,某种程度上也可以提高开发效率。从软件设计上,对程序员的要求比较高。

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

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

相关文章

Python实现哈里斯鹰优化算法(HHO)优化支持向量机回归模型(SVR算法)项目实战

说明:这是一个机器学习实战项目(附带数据代码文档视频讲解),如需数据代码文档视频讲解可以直接到文章最后获取。 1.项目背景 2019年Heidari等人提出哈里斯鹰优化算法(Harris Hawk Optimization, HHO),该算法有较强的全…

目标检测——day66 Scaled-YOLOv4: Scaling Cross Stage Partial Network

Scaled-Yolov4:可伸缩跨级部分网络 Scaled-YOLOv41. Introduction2. Related work2.1. Real-time object detection2.2. Model scaling(模型缩放)3. Principles of model scaling4. Scaled-YOLOv44.1. CSP-ized YOLOv44.2. YOLOv4-tiny4.3. YOLOv4-large…

上海亚商投顾:沪指缩量小幅调整 半导体与旅游股领涨

上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。市场情绪沪指今日缩量小幅调整,创业板指稍显强势,多数时间红盘运行,科创50指数涨超1%。半…

springMVC讲解(上)

SpringMvc 1,简介 springmvc是spring的一个后续产品,是spring的一个子项目,是spring为表示层所开发的一整套完备的解决方案,在表示层框架经历了struct、webWork、struct2等诸多产品的历代更迭之后,目前业界普遍选择了…

JAVA就业课程,面试题大全

面试整体流程1.1 简单的自我介绍我是xxxx,工作xxx年.我先后在xxxx公司、yyyy公司工作。先后做个xxxx项目、yyyy项目。1.2 你简单介绍一下xxxx项目为了解决xxxx问题,开发了一套xxxx系统,该系统主要有那些部分组成。简单介绍项目的整体架构。参与某个模块的…

【Python学习】输入和输出

前言 往期文章 【Python学习】列表和元组 【Python学习】字典和集合 【Python学习】条件和循环 在很多时候,你会想要让你的程序与用户(可能是你自己)交互。你会从用户那里得到输入,然后打印一些结果。我们可以使用iinput和pr…

MATLAB趣味绘图-内接正六边形旋转

MATLAB趣味绘图-内接正六边形旋转 观察一下内部的正六边形大概在外部的正六边形边的四等分点的位置,通过数学平面几何知识可得边和角度的迭代关系式为: an134an−1θnθn−1arctan⁡36a_n \frac{\sqrt{13}}{4} a_{n-1} \\ \theta_n \theta_{n-1}\arcta…

金融行业数据库场景下,SmartX 超融合表现如何|性能验证与落地实践

在金融行业,数据库是一项至关重要的 IT 基础设施。作为交易和数据的主要载体,数据库往往需要在短时间内处理大量的业务数据,其可靠性、稳定性和性能将直接影响业务系统的运行状态。而在进行数据库基础架构选型时,一些客户对于超融…

C++学习笔记——类和对象

1.面向对象的三大特性:封装、继承、多态 2.对象有其属性和行为 3.具有相同性质的对象,可被抽象为类 1.封装 1.封装是C面向对象三大特性之一 2.封装的意义: (1)将属性(变量)和行为&#xff…

python学习 --- 字典基础

目录 一、什么是字典? 1、字典示意图-无序说明 2、字典实现原理 二、字典的创建 1、使用花括号 2、使用内置函数dict() 三、字典常用操作 1、字典中元素的获取 2、key的判断(存在与否) 3、字典元素的删除 4、字典元素的新增 5、获…

计讯物联智慧水务解决方案:用“智水”捍卫生命之泉

项目背景 水是生命的源泉,是城市的灵魂,是农业的命脉,是工业的基石。2022年以来,基于国家政策的引导、科技革新的驱动与供排水需求增长,智慧水务的发展突飞猛进,从信息化到数字化,再到智能化&a…

若依 ruoyi vue el-switch 列表开关状态显示有误 全部关闭的问题

后台使用int类型传状态status的值但是前端列表展示的开关状态是未开启,实际上,后台传的都是开启的状态结果应该是这样确定后台传的status值 在 el-switch 标签中是否使用了正确的值判断,比如 后台用的是字符串、布尔 或者是 数值类型&#xf…

android判断文件是否存在跳转不同activity

android studio版本:2021.2.1Patch 2例程名称:ActivityJump完成日期:2023.1.17一直在完善一个小东西,也是不断的在学习。之前做的那个桌面日历天气(老旧安卓手机发挥余热做桌面时钟摆件使用),有…

java 数列排序

试题 基础练习 数列排序提交此题 评测记录 资源限制内存限制&#xff1a;512.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s问题描述给定一个长度为n的数列&#xff0c;将这个数列按从小到大的顺序排列。1<n<200输入格式…

【渗透测试】信息搜集总结

前言零零散散的发布过很多文章了&#xff0c;但是也没有完整的总结一下&#xff0c;今天就从信息搜集入手&#xff0c;系统的总结一下。一方面可以巩固自己的基础&#xff0c;作为自己的字典随用随查&#xff0c;另一方面希望对大家的学习和工作起到帮助作用。按照这个过程基本…

求两点间的距离-C语言实现

任务描述 已知两点A(x1,y1),B(x2,y2),求其间的距离。 输入 一行四个浮点数,x1,y1,x2,y2 输出 两点间的距离,要求不要输出多余的零。 本关知识 两点间的距离公式 pow函数 在C语言中,提供了用于求x的y次幂的函数,函数原型为: double pow(double x, double y…

啊啊啊小红书爆款标题技巧被我找到了!

我通过平日里对小红书的了解和积累&#xff0c;再加上这一阵子对小红书爆款笔记的研究&#xff0c;终于在这么多笔记当中发现了小红书爆款笔记标题的撰写规律&#xff01; 我们在撰写小红书的时候恨不得篇篇都是爆文&#xff0c;那么今天就来讲一下小红书那些爆款笔记标题的撰写…

75、DiffRF: Rendering-Guided 3D Radiance Field Diffusion

简介 主页&#xff1a;https://sirwyver.github.io/DiffRF/ 对应用于三维亮度场的概率扩散过程进行去噪。在3D监控和体积渲染的指导下&#xff0c;模型能够无条件地合成高保真3D资产(左)。 蒙面补全的新应用(右)&#xff0c;即从不完整的对象中恢复形状和外观的任务(在右上方…

超实用的百度百科人物词条创建攻略分享,纯干货

自媒体时代&#xff0c;人们越来越有IP意识&#xff0c;打造个人IP就是在为自己创造更多价值。 个人IP的打造是一个提升知名度的过程&#xff0c;怎么才能快速提升&#xff1f;创建一个百度百科词条不失为一个好的选择。 现在用户有问题就会在百度上搜索一下&#xff0c;当用户…

java实现模拟调用接口

本文总结如何用fiddler和postman调试接口&#xff0c;并用java模拟调用接口。fiddler用法当页面点击事件后&#xff0c;在fiddler出现一个请求&#xff0c;单击左侧请求&#xff0c;在右侧的raw的tab标签&#xff0c;出现该请求的详细内容。其实一个请求需要两类参数&#xff0…