建造者模式-复杂对象的组装与创建

news2024/10/7 12:25:58

 生产一辆车,主要有以下步骤:安装骨架、安装发动机及安装轮胎。这些步骤有指定的执行顺序,步骤缺一不可。

图 传统方案

传统方案存在的问题:

  1. 传参不便,虽可在构造函数那传参,但是传参时需要注意参数顺序等。
  2. 扩展不便,例如新增一种车型,创建步骤不同,这时需要修改Car的源码,不符合开闭原则。
  3. 客户端在创建对象的时候,需要关心内部的创建细节。对象与对象创建耦合在一起。

1 建造者模式

将客户端与包含多个组成部分(或部件)的复杂对象的创建过程分离,客户端无需关心复杂对象的内部组成部分与装配方式,只需要知道所需的建造者类型即可。

创建者模式关注如何一步步地创建一个复杂对象,不同的具体建造者定义了不同的创建过程,且具体的建造者相互独立,增加新的建造者非常方便,无须修改已有代码,系统具有较好的扩展性。

图 建造者模式结构图

Product: 产品角色,是被构建的复杂对象,包含多个部件。具体建造者创建该产品的内部表示并定义其装配过程。

Builder: 抽象建造者,为创建一个产品Product对象的各个部件指定抽象接口。一般声名两类方法:一类方法是buildPartX(),用于创建复杂对象的各个部件;另一类方法是getResult(),用于返回复杂对象。Builder既可以是抽象类,也可以是接口。

ConcreteBuilder: 具体建造者,实现Builder接口,实现各个部件的具体构造和装配方法,定义并明确其所创建的复杂对象,也可以提高一个方法返回创建好的复杂产品对象。

Director: 指挥者,又称为导演类,负责安排负责对象的建造次序。在其construct()方法中调用建造者对象的部件构造与装配方法,完成复杂对象的建造。客户端一般只需要与指挥者进行交互。

public class Car {

    private String body;

    private String engine;

    private String tire;

    public void setBody(String body) {
        this.body = body;
    }

    public void setEngine(String engine) {
        this.engine = engine;
    }

    public void setTire(String tire) {
        this.tire = tire;
    }

    @Override
    public String toString() {
        return "Car{" +
                "body='" + body + '\'' +
                ", engine='" + engine + '\'' +
                ", tire='" + tire + '\'' +
                '}';
    }
}

public abstract class CarBuilder {

    protected final Car car = new Car();

    public abstract void installBody();

    public abstract void installEngine();

    public abstract void installTire();

    public Car getResult() {
        return car;
    }

}

public class AudiCarBuilder extends CarBuilder{

    @Override
    public void installBody() {
        car.setBody("奥迪-车身");
    }

    @Override
    public void installEngine() {
        car.setEngine("奥迪-发动机");
    }

    @Override
    public void installTire() {
        car.setTire("奥迪-轮胎");
    }

}

public class BenzCarBuilder extends CarBuilder{

    @Override
    public void installBody() {
        car.setBody("奔驰-车身");
    }

    @Override
    public void installEngine() {
        car.setEngine("奔驰-发动机");
    }

    @Override
    public void installTire() {
        car.setTire("奔驰-轮胎");
    }
}

public class CarDirector {

    private final CarBuilder carBuilder;

    public CarDirector(CarBuilder carBuilder) {
        this.carBuilder = carBuilder;
    }

    public Car construct() {
        carBuilder.installBody();
        carBuilder.installEngine();
        carBuilder.installTire();
        return carBuilder.getResult();
    }
}

public class Client {

    public static void main(String[] args) {
        CarBuilder carBuilder;
        carBuilder = new BenzCarBuilder();

        CarDirector carDirector = new CarDirector(carBuilder);
        System.out.println(carDirector.construct());
    }

}

1.1 省略Director

在有些情况下,为了简化系统结构,可以将Director和抽象建造者Builder进行合并,在Builder中提供逐步构建复杂产品对象的construct()方法。将上面的Builder及Client类修改为:

public abstract class OmitCarBuilder {

    protected final Car car = new Car();

    public abstract void installBody();

    public abstract void installEngine();

    public abstract void installTire();

    public Car construct() {
        installBody();
        installEngine();
        installTire();
        return car;
    }

}

public class Client {

    public static void main(String[] args) {
        OmitCarBuilder omitCarBuilder = new AudiOmitCarBuilder();
        System.out.println(omitCarBuilder.construct());
    }

}

省略方式不影响系统的灵活性和可扩展性,同时还简化了系统结构,但加重了抽象建造者类的职责。如果construct()方法较为复杂,待构建产品的组成部分较多,建议还是将construct()方法单独封装在Director中。这样更符合单一职责原则。

1.2 钩子方法

增加钩子方法来控制是否调用某个buildPartX()方法。

构造方法的返回类型通常为boolean,方法名一般为isxxx (xxx为属性名)。构造方法定义在抽象建造者类中。构造方法来决定某属性(组件)是否需要添加。

例如,上面需求做了修改:奥迪车不需要装发动机,而奔驰车需要安装。

public abstract class HookCarBuilder {

    protected Car car = new Car();

    public abstract void installBody();

    public abstract void installEngine();

    public abstract void installTire();

    public boolean isBody() {
        return false;
    }

    public boolean isEngine() {
        return false;
    }

    public boolean isTire() {
        return false;
    }

    public Car construct() {
        if (!isBody()) installBody();
        if (!isEngine()) installEngine();
        if (!isTire()) installTire();
        return car;
    }

}

public class AudiHookCarBuilder extends HookCarBuilder{
    @Override
    public void installBody() {
        car.setBody("奥迪-车身");
    }

    @Override
    public void installEngine() {
        car.setEngine("奥迪-发动机");
    }

    @Override
    public void installTire() {
        car.setTire("奥迪-轮胎");
    }

    @Override
    public boolean isEngine() {
        return true;
    }
}

通过钩子方法,可以对复杂产品的构建进行精细控制。可以控制buildPartX()方法的执行顺序,还可以控制是否需要执行某个buildPartX()方法。

2 优缺点

优点:

  1. 客户端不必知道产品内部组成细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同产品对象。
  2. 每个具体建造者都相对独立,而与其他具体建造者无关。用户使用不同的建造者即可生成不同的对象。系统扩展方便,符合开闭原则。
  3. 可以更加精细化地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便控制。

缺点:

  1. 所创建的产品一般具有较多的共同点,其组成部分相似。如果产品之间的差异性很大,就不适合使用建造者模式。
  2. 如果产品内部结构复杂且多变,可能需要定义很多具体建造者类来实现这种变化,这会让系统变得臃肿。

3 适用场景

  1. 需要生成的产品对象有复杂的内部结构,包含多个成员变量。
  2. 生成的产品对象属性相互依赖,需要指定其生成顺序。
  3. 隔离复杂对象的创建和适用,并使得相同的创建过程可以创建不同的产品。

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

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

相关文章

After Effects CPU 和 RAM 使用率高,如何修复?

如果您发现 Adob​​e After Effects 的 CPU 和 RAM 使用率较高,可以按照以下方法解决该问题。 1]确保您的系统满足最低系统要求 要在您的 PC 上运行 Adob​​e After Effects,您的 PC 需要满足最低系统要求。只有这样,该程序才会停止消耗更…

WEB:Confusion1

背景知识 SSTI漏洞 题目 根据网站图片和题目描述的提示,大象是php,蟒蛇是python,说明了这个网站是用python写的 在python中,比较常规的漏洞就是SSTI模板注入 没有思路,先点login和register页面看看 查看源代码 之前…

如何防止DDoS?基于分布式云的 DDoS 解决方案一览

在日益开放和互联的世界中,DDOS(分布式拒绝服务)攻击和安全漏洞日益频发,企业都应将有效地保护其业务、声誉和数据中心免受不断加剧的DDoS攻击放在战略性位置。如何防止DDoS?看了F5提供的分布式云DDoS解决方案&#xf…

list最常用的遍历五种方式以及使用场景

目录 遍历方式的适用场景对比 迭代器遍历 列表迭代器 增强for遍历 Lambda表达式 lambda表达式简介 普通for遍历 集合中通用的并且常用的六种方法 遍历方式的适用场景对比 迭代器遍历 :在遍历过程中需要删除元素,请使用迭代器 列表迭代器&#xff1…

Java如何实现定时读取json文件里的内容

Java实现定时读取json文件里的内容 项目背景代码实现读取json配置文件定时任务 测试 项目背景 有时候我们会需要定时来读取JSON配置文件里的内容,来执行一些业务逻辑上的操作。 比如:开发一个物流运输系统,系统需要定期读取一个包含货物信息…

6个真正免费的平面设计素材网站

新手设计师没有自己的素材库,找素材费时又费力,找到了还不一定能免费下载或商用,别急!我带着我收藏多年的素材库来了,平面、UI、免扣、高清背景图、设计元素、字体等素材全部都有,重点是免费下载&#xff0…

线程池学习(四)任务调度

线程池有几个重要的属性,核心线程数,最大线程数,阻塞任务队列。 一、调度流程 1. 接收新的任务后,先判断核心线程数是否已满,未满则创建新的线程去执行任务 2. 如果核心线程数已满,再判断阻塞任务队列是否…

Vue Router相关理解2

路由的query参数 传递参数 <!-- <router-link to"/home/message/detail?id666&title你好啊">{{ m.title }}</router-link> --><!-- 跳转路由并携带query参数&#xff0c;to的字符串写法 --><!-- <router-link :to"/home/m…

MySql如何卸载干净经验分享

第一步&#xff1a;首先打开注册表&#xff1a;点击电脑的开始按钮&#xff0c;打开找到运行&#xff0c;输入regedit&#xff0c;进入注册表&#xff1b; 第二步&#xff1a;删除mysql再注册表中的信息&#xff0c;以下三个目录&#xff1a; 1.HKEY_LOCAL_MACHINE\SYSTEM\Cont…

Unity中指定物体的模型面数分析

给定一架飞机模型&#xff0c;需要分析该模型中&#xff0c;各个3D物体的面数和三角形数&#xff0c;目的是用于观察哪些物体面数过多&#xff0c;需要减面。 一、模型面数分析 二、脚本代码 using System.Collections; using System.Collections.Generic; using UnityEngine…

用VMware运行linux CentOS7时,Network中没有wired选项,只有VPN的情况解决方案

毫无征兆&#xff0c;平时使用正常的CentOS7在今天打开后发现无法连接到网络&#xff0c;wired图标也莫名的消失&#xff0c;并且在打开网络设置&#xff0c;也没有对wired的设置模块&#xff0c;这种问题很有可能是之前对云端之类的源进行操纵以及主机上挂梯子等一系列情况综合…

fastadmin+python+mysql +wxbot实现万能模糊查询(和chatgpt一起完成的)

废话不多说直接上代码&#xff1a; 功能&#xff0c;fastadmin后台管理这些机房服务器的信息&#xff0c;wxbot 通过/指令任意字段的信息查询 让wxbot去数据库里查询相关的信息&#xff0c;在通过wx发送给你。 1.创建数据库 CREATE TABLE fa_databank (ID INT AUTO_INCREMEN…

简单认识MySQL数据库日志和数据的备份恢复

文章目录 Mysql 备份与还原一、数据备份的重要性二、数据库备份类型1 、物理备份2 、逻辑备份 三、常见的备份方法1、 物理冷备2、 专用备份工具 mysqldump 或 mysqlhotcopy3、 启用二进制日志进行增量备份3.4 第三方工具备份 四、MySQL完全备份1、简介2、优点&#xff1a;3、缺…

避雷!7月有4本SCIE期刊被剔除!(附目录下载)

2023年7月SCI、SSCI期刊目录更新 2023年7月17日&#xff0c;科睿唯安更新了WOS期刊目录&#xff0c;继上次6月WOS期刊目录剔除3本SCIE&SSCI期刊之后&#xff0c;此次7月更新又有4本SCIE期刊发生变动&#xff0c;其中有3本期刊被踢出SCIE数据库&#xff0c;1本期刊更改了名…

通过new FormData提交简单数据

通过new FormData提交简单数据 效果示例图代码 效果示例图 代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style type"text/css">* {padding: 0px;margin: 0px;box-sizing: border-…

CSS 渐变边框及动画

转载请注明出处&#xff0c;点击此处 查看更多精彩内容 用 CSS 实现渐变边框及动画&#xff0c;下面对关键点进行解释说明&#xff0c;查看完整代码及预览效果请 点击这里。 简单说明原理&#xff1a;使用伪元素 ::before 绘制一个渐变色&#xff0c;然后使用伪元素 ::after 绘…

详解Windows安装分布式版本控制系统git

文章目录 前言下载安装相关链接 前言 git是一个分布式版本控制软件&#xff0c;最初由Linux创作者Linus Torvalds创作&#xff0c;并于2015年以GPL许可协议发布。git易于学习&#xff0c;占用空间小&#xff0c;性能却快如闪电&#xff0c;可以快速、 高效的管理从小到大的项目…

STM32 HAL/STD库驱动HC-SR04测距

STM32 HAL/STD库驱动HC-SR04测距 ✨说明:本文不介绍HC-SR04原理。 &#x1f4cc;相关篇《STM32F103VCDS18B20温度hc-sr04超声波测距I2C OLED显示》 &#x1f33f;HAL库实现方法比较简易&#xff0c;只需配置2个IO引脚&#xff1a;一个配置为输入&#xff0c;一个配置为输出即可…

【正点原子STM32连载】第六十四章 UCOSII实验2-信号量和邮箱摘自【正点原子】STM32F103 战舰开发指南V1.2

1&#xff09;实验平台&#xff1a;正点原子stm32f103战舰开发板V4 2&#xff09;平台购买地址&#xff1a;https://detail.tmall.com/item.htm?id609294757420 3&#xff09;全套实验源码手册视频下载地址&#xff1a; http://www.openedv.com/thread-340252-1-1.html# 第六…

【C++】多线程交替打印奇偶数

目录 版本1 双信号量版 版本二 单信号量版 版本三 信号量版 共享资源是100个数字&#xff08;一个计数器的 由两个进程争抢完成&#xff09; 首先访问临界资源&#xff08;对计数器操作&#xff09;是肯定的要加锁的&#xff0c;交替打印肯定要用条件变量来互相唤醒 互相锁死…