【再谈设计模式】装配器模式 ~复杂结构构建的巧匠

news2024/11/29 5:25:16

一、引言

        在软件开发过程中,创建复杂对象往往是一项具有挑战性的任务。传统的直接实例化对象的方式在面对复杂对象构建时,可能会导致代码的可维护性、可读性和扩展性变差。这时候,我们就需要一些设计模式来优雅地解决这些问题,装配器设计模式就是其中一种强大的解决方案。它提供了一种有条理的方式来构建复杂对象,使得对象的构建过程更加清晰、灵活并且易于管理。

二、定义与描述

        装配器设计模式是一种创建复杂对象的设计模式,它将对象的构建过程从对象本身分离出来,通过一个装配器(Builder)来逐步构建复杂对象的各个部分,最终组装成完整的对象。

        这种模式允许我们按照特定的步骤和顺序构建对象,并且可以在构建过程中对各个部分进行定制化操作。例如,在构建一个复杂的汽车对象时,我们可以先构建引擎部分,然后是车身部分,接着是内饰部分,最后将这些部分组装在一起形成完整的汽车。

三、抽象背景

        在许多应用场景中,复杂对象的构建往往涉及到多个不同类型的组件或者属性的设置。这些组件可能具有复杂的初始化逻辑或者依赖关系。如果将所有的构建逻辑都放在对象的构造函数中,那么构造函数会变得非常臃肿,难以理解和维护。而且,当需要对对象的构建过程进行修改或者扩展时,直接修改构造函数可能会影响到其他使用该对象的代码。

        装配器设计模式的出现就是为了将这些复杂的构建逻辑从对象本身抽象出来,通过一个独立的装配器来管理对象的构建过程,从而提高代码的可维护性和扩展性。

四、适用场景与现实问题解决

(一)适用场景

  • 复杂对象构建
    • 当需要创建的对象具有多个属性,并且这些属性的设置具有一定的顺序或者依赖关系时,装配器设计模式非常适用。例如,构建一个数据库连接对象,需要设置数据库类型、主机地址、端口号、用户名和密码等多个属性,而且这些属性的设置顺序和正确性对数据库连接的成功与否至关重要。
  • 对象构建过程的定制化
    • 如果对于同一个对象,根据不同的需求或者条件需要有不同的构建方式,装配器设计模式可以很好地满足这种需求。比如,构建一个用户界面组件,根据不同的用户权限或者设备类型(桌面端、移动端),可以定制不同的构建过程,如添加不同的功能按钮或者布局方式。

(二)现实问题解决

  • 提高代码可读性
    • 在没有装配器设计模式时,复杂对象的构建逻辑可能分散在多个地方或者嵌套在一个庞大的构造函数中。使用装配器设计模式后,构建过程被清晰地分解为多个步骤,每个步骤都有明确的目的,使得代码的可读性大大提高。
  • 易于维护和扩展
    • 当需要对复杂对象的构建逻辑进行修改或者添加新的属性时,只需要在装配器中进行相应的修改,而不需要在对象的构造函数或者其他使用该对象的地方进行大规模的改动。

五、装配器模式的现实生活的例子

  • 建筑房屋
    • 建筑一座房屋可以看作是构建一个复杂对象。首先,建筑工人会构建地基部分(类似于对象的一个组件构建),然后是框架结构,接着是墙壁、屋顶等部分。每个部分都有其特定的构建过程和要求,最后将这些部分组合在一起就形成了完整的房屋。这里的建筑工人就可以看作是装配器,他们按照一定的顺序和方法构建房屋的各个部分并将其组装起来。

  • 组装电脑
    • 组装一台电脑时,我们首先会安装CPU(可能需要涂抹散热硅脂等操作),然后是安装内存、硬盘、主板等组件,最后将机箱组装起来并连接各种线缆。这个过程中,电脑组装员就像是装配器,他们逐个安装电脑的各个组件,最终构建出一台完整的电脑。

六、初衷与问题解决

  • 初衷
    • 装配器设计模式的初衷是为了将复杂对象的构建过程从对象本身分离出来,以提高代码的可维护性、可读性和扩展性。通过将构建逻辑封装在一个独立的装配器中,使得对象的构建过程更加清晰,易于理解和管理。
  • 问题解决
    • 解决了复杂对象构建时构造函数过于臃肿的问题。在传统方式下,复杂对象的构造函数可能需要接受多个参数并进行复杂的初始化逻辑,这使得构造函数难以维护和扩展。装配器设计模式将这些逻辑分散到各个构建步骤中,使得代码结构更加清晰。同时,也解决了构建过程定制化的问题,通过不同的装配器实现或者装配器的不同使用方式,可以轻松实现对象构建的定制化。

七、代码示例

(一)Java示例

类图:

代码:

// 产品类:汽车
class Car {
    private String engine;
    private String body;
    private String interior;

    public Car(String engine, String body, String interior) {
        this.engine = engine;
        this.body = body;
        this.interior = interior;
    }

    public String getEngine() {
        return engine;
    }

    public String getBody() {
        return body;
    }

    public String getInterior() {
        return interior;
    }
}

// 装配器接口
interface CarBuilder {
    CarBuilder buildEngine(String engine);
    CarBuilder buildBody(String body);
    CarBuilder buildInterior(String interior);
    Car getCar();
}

// 具体装配器实现
class ConcreteCarBuilder implements CarBuilder {
    private String engine;
    private String body;
    private String interior;

    @Override
    public CarBuilder buildEngine(String engine) {
        this.engine = engine;
        return this;
    }

    @Override
    public CarBuilder buildBody(String body) {
        this.body = body;
        return this;
    }

    @Override
    public CarBuilder buildInterior(String interior) {
        this.interior = interior;
        return this;
    }

    @Override
    public Car getCar() {
        return new Car(engine, body, interior);
    }
}

// 导演类(可选,用于控制构建过程)
class Director {
    public Car constructCar(CarBuilder builder) {
        return builder.buildEngine("V8")
              .buildBody("SUV Body")
              .buildInterior("Luxury Interior")
              .getCar();
    }
}
  • Car 类表示最终要构建的产品,它有发动机、车身和内饰等私有属性,并提供了相应的构造函数和获取属性的方法。
  • CarBuilder 接口定义了构建汽车各个部件的方法以及获取最终构建好的汽车的方法。
  • ConcreteCarBuilder 类实现了 CarBuilder 接口,具体实现了构建汽车各个部件的逻辑,并能根据已设置的部件信息返回构建好的 Car 实例。
  • Director 类是可选的,用于控制构建过程,它通过调用 CarBuilder 的一系列构建方法来构建出特定配置的汽车。

        通过这种设计模式,可以将复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同表示的对象,并且构建过程更加灵活和可维护。

使用流程图:

(二)C++示例

#include <iostream>
#include <string>

// 产品类:汽车
class Car {
private:
    std::string engine;
    std::string body;
    std::string interior;
public:
    Car(const std::string& engine, const std::string& body, const std::string& interior) :
        engine(engine), body(body), interior(interior) {}

    const std::string& getEngine() const {
        return engine;
    }

    const std::string& getBody() const {
        return body;
    }

    const std::string& getInterior() const {
        return interior;
    }
};

// 装配器类
class CarBuilder {
protected:
    std::string engine;
    std::string body;
    std::string interior;
public:
    virtual CarBuilder& buildEngine(const std::string& engine) {
        this->engine = engine;
        return *this;
    }

    virtual CarBuilder& buildBody(const std::string& body) {
        this->body = body;
        return *this;
    }

    virtual CarBuilder& buildInterior(const std::string& interior) {
        this->interior = interior;
        return *this;
    }

    virtual Car build() = 0;
};

// 具体装配器实现
class ConcreteCarBuilder : public CarBuilder {
public:
    Car build() override {
        return Car(engine, body, interior);
    }
};

// 导演类(可选,用于控制构建过程)
class Director {
public:
    Car constructCar(CarBuilder* builder) {
        return builder->buildEngine("V8")
               ->buildBody("SUV Body")
               ->buildInterior("Luxury Interior")
               ->build();
    }
};

(三)Python示例

# 产品类:汽车
class Car:
    def __init__(self, engine, body, interior):
        self.engine = engine
        self.body = body
        self.interior = interior

    def get_engine(self):
        return self.engine

    def get_body(self):
        return self.body

    def get_interior(self):
        return self.interior


# 装配器类
class CarBuilder:
    def __init__(self):
        self.engine = None
        self.body = None
        self.interior = None

    def build_engine(self, engine):
        self.engine = engine
        return self

    def build_body(self, body):
        self.body = body
        return self

    def build_interior(self, interior):
        self.interior = interior
        return self

    def get_car(self):
        return Car(self.engine, self.body, self.interior)


# 导演类(可选,用于控制构建过程)
class Director:
    def construct_car(self, builder):
        return builder.build_engine('V8') \
              .build_body('SUV Body') \
              .build_interior('Luxury Interior') \
              .get_car()

(四)Go示例

// 产品类:汽车
type Car struct {
    engine   string
    body     string
    interior string
}

func NewCar(engine, body, interior string) *Car {
    return &Car{
       engine:   engine,
       body:     body,
       interior: interior,
    }
}

func (c *Car) GetEngine() string {
    return c.engine
}

func (c *Car) GetBody() string {
    return c.body
}

func (c *Car) GetInterior() string {
    return c.interior
}

// 装配器接口
type CarBuilder interface {
    BuildEngine(string) CarBuilder
    BuildBody(string) CarBuilder
    BuildInterior(string) CarBuilder
    GetCar() *Car
}

// 具体装配器实现
type concreteCarBuilder struct {
    engine   string
    body     string
    interior string
}

func (c *concreteCarBuilder) BuildEngine(engine string) CarBuilder {
    c.engine = engine
    return c
}

func (c *concreteCarBuilder) BuildBody(body string) CarBuilder {
    c.body = body
    return c
}

func (c *concreteCarBuilder) BuildInterior(interior string) CarBuilder {
    c.interior = interior
    return c
}

func (c *concreteCarBuilder) GetCar() *Car {
    return NewCar(c.engine, c.body, c.interior)
}

// 导演类(可选,用于控制构建过程)
type Director struct{}

func (d *Director) ConstructCar(builder CarBuilder) *Car {
    return builder.BuildEngine("V8")
      .BuildBody("SUV Body")
      .BuildInterior("Luxury Interior")
      .GetCar()
}

八、装配器模式的优缺点

(一)优点

  • 构建过程的清晰性
    • 如前面所述,通过将复杂对象的构建过程分解为多个步骤,每个步骤由装配器的一个方法来表示,使得构建过程清晰可见,易于理解。
  • 对象构建的灵活性
    • 可以根据不同的需求,通过不同的装配器实现或者装配器的不同使用方式,轻松实现对象构建的定制化。例如,可以根据不同的用户需求构建不同配置的产品对象。
  • 代码的可维护性和扩展性
    • 当需要对对象的构建过程进行修改,如添加新的属性或者修改某个属性的构建逻辑时,只需要在装配器中进行相应的修改,而不需要对整个对象的构造函数或者使用该对象的其他代码进行大规模的改动。

(二)缺点

  • 增加代码复杂度
    • 引入装配器设计模式会增加一定的代码量,因为需要创建装配器类、产品类以及可能的导演类等。对于简单的对象构建场景,可能会显得过于复杂。
  • 需要更多的设计考虑
    • 在使用装配器设计模式时,需要仔细考虑装配器与产品类之间的关系、构建步骤的划分以及装配器的复用性等问题,如果设计不当,可能会导致代码难以理解和维护。

九、适配器设计模式的升级版

        装配器设计模式可以看作是适配器设计模式的一种升级版(在某种意义上)。适配器设计模式主要解决的是接口不兼容的问题,将一个类的接口转换成客户期望的另一个接口。而装配器设计模式在此基础上更进一步,不仅可以处理接口的转换(在构建复杂对象时可能涉及到不同组件接口的适配),还专注于复杂对象的构建过程。

        装配器设计模式通过将对象的构建过程分离出来,在构建过程中可以更灵活地处理不同组件之间的适配关系,并且可以根据不同的需求构建出具有不同特性的对象,这是相比于传统适配器设计模式在处理复杂对象构建方面的一种扩展和升级。

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

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

相关文章

牛客周赛 Round 69 C-E

C——仰望水面的歪 一、题目描述&#xff1a; 一看这个题目是不是觉得是物理问题&#xff0c;我也觉得是这样的&#xff0c;全反射我都快忘记了&#xff0c;结果发现他居然还能这样看&#xff0c;请看图片&#xff1a; 第一种情况&#xff1a;当目标点在小歪所在平面的上面得…

【C++】C++新增特性解析:Lambda表达式、包装器与绑定的应用

V可变参数模板与emplace系列 C语法相关知识点可以通过点击以下链接进行学习一起加油&#xff01;命名空间缺省参数与函数重载C相关特性类和对象-上篇类和对象-中篇类和对象-下篇日期类C/C内存管理模板初阶String使用String模拟实现Vector使用及其模拟实现List使用及其模拟实现…

狂野飙车8+(Asphalt 8+) for Mac 赛车竞速游戏 安装教程

Mac分享吧 文章目录 狂野飙车8(Asphalt 8) for Mac 赛车竞速游戏软件 效果图展示一、狂野飙车8(Asphalt 8) 赛车竞速游戏 Mac电脑版——v2.1.11️⃣&#xff1a;下载软件2️⃣&#xff1a;安装软件2.1 左侧安装包拖入右侧文件夹中&#xff0c;等待安装完成&#xff0c;运行软件…

共享售卖机语音芯片方案选型:WTN6020引领智能化交互新风尚

在共享经济蓬勃发展的今天&#xff0c;共享售卖机作为便捷购物的新形式&#xff0c;正逐步渗透到人们生活的各个角落。为了提升用户体验&#xff0c;增强设备的智能化和互动性&#xff0c;增加共享售卖机的语音功能就显得尤为重要。 共享售卖机语音方案选型&#xff1a; WTN602…

.net core MVC入门(一)

文章目录 项目地址一、环境配置1.1 安装EF core需要包1.2 配置数据库连接二、使用EF创建表2.1 整体流程梳理2.1 建表详细流程2.1.1 创建一个Category的Model2.1.2 创建Category的EF实体三、添加Category页面的视图3.1整体流程梳理3.2 添加Controller3.3 添加View 视图四、使用E…

vue修改el-table-column背景色和上下margin外边距调整行间距方法教程

在vue中需要修改el-table表格中每一行el-table-column背景色&#xff0c;还有table表格中数据太多&#xff0c;看起来臃肿&#xff0c;需要margin调整上下边距&#xff0c;直接使用margin不生效&#xff0c;是因为display使用的是table属性&#xff0c;所以我们要利用table特性…

Spring框架整合单元测试

目录 一、配置文件方式 1.导入依赖 2.编写类和方法 3.配置文件applicationContext-test.xml 4.测试类 5.运行结果 二、全注解方式 1.编写类和方法 2.配置类 3.测试类 4.运行结果 每次进行单元测试的时候&#xff0c;都需要编写创建工厂&#xff0c;加载配置文件等相关…

微信小程序按字母顺序渲染城市 功能实现详细讲解

在微信小程序功能搭建中&#xff0c;按字母渲染城市会用到多个ES6的方法&#xff0c;如reduce&#xff0c;map&#xff0c;Object.entries()&#xff0c;Object.keys() &#xff0c;需要组合熟练掌握&#xff0c;才能优雅的处理数据完成渲染。 目录 一、数据分析 二、数据处理 …

可视化建模与UML《状态图实验报告》

其实我并不是一个实验报告博主。 大家收拾收拾准备期末复习了嗷&#xff0c;差不多了&#xff0c;不想太赶可以开始准备了。 一、实验目的&#xff1a; 1、熟悉状态图的基本功能和使用方法。 2、掌握使用建模工具软件绘制状态图的方法 二、实验环境&#xff1a; window7 | 10…

单片机学习笔记 11. 外部中断

更多单片机学习笔记&#xff1a;单片机学习笔记 1. 点亮一个LED灯单片机学习笔记 2. LED灯闪烁单片机学习笔记 3. LED灯流水灯单片机学习笔记 4. 蜂鸣器滴~滴~滴~单片机学习笔记 5. 数码管静态显示单片机学习笔记 6. 数码管动态显示单片机学习笔记 7. 独立键盘单片机学习笔记 8…

Ubuntu下的Graphviz的基础使用方法

一、Graphviz介绍 graphviz是贝尔实验室开发的一个开源的工具包&#xff0c;它使用一个特定的DSL(领域特定语言):dot作为脚本语言&#xff0c;然后使用布局引擎来解析此脚本&#xff0c;并完成自动布局 1、什么是Graphviz 官网地址&#xff0c;https://www.graphviz.org/ Gr…

IDEA全局设置-解决maven加载过慢的问题

一、IDEA全局设置 注意&#xff1a;如果不是全局设置&#xff0c;仅仅针对某个项目有效&#xff1b;例在利用网上教程解决maven加载过慢的问题时&#xff0c;按步骤设置却得不到解决&#xff0c;原因就是没有在全局设置。 1.如何进行全局设置 a.在项目页面&#xff0c;点击f…

#渗透测试#红蓝攻防#HW#经验分享#溯源反制

免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停止本文章阅 目录 一、溯源反制 1、溯源反制的重要性 2、溯源…

Level DB --- SkipList

class SkipList class SkipList 是Level DB中的重要数据结构&#xff0c;存储在memtable中的数据通过SkipList来存储和检索数据&#xff0c;它有优秀的读写性能&#xff0c;且和红黑树相比&#xff0c;更适合多线程的操作。 SkipList SkipList还是一个比较简单的数据结构&a…

YOLOv11融合PIDNet中的PagFM模块及相关改进思路

YOLOv11v10v8使用教程&#xff1a; YOLOv11入门到入土使用教程 YOLOv11改进汇总贴&#xff1a;YOLOv11及自研模型更新汇总 《PIDNet: A Real-time Semantic Segmentation Network Inspired by PID Controllers》 一、 模块介绍 论文链接&#xff1a;https://arxiv.org/pdf/2…

跨平台应用开发框架(4)-----Qt(样式篇)

目录 1.QSS 1.基本语法 2.QSS设置方式 1.指定控件样式设置 2.全局样式设置 1.样式的层叠特性 2.样式的优先级 3.从文件加载样式表 4.使用Qt Designer编辑样式 3.选择器 1.类型选择器 2.id选择器 3.并集选择器 4.子控件选择器 5.伪类选择器 4.样式属性 1.盒模型 …

uniapp定义new plus.nativeObj.View实现APP端全局弹窗

为什么要用new plus.nativeObj.View在APP端实现弹窗&#xff1f;因为uni.showModal在APP端太难看了。 AppPopupView弹窗函数参数定义 参数一:弹窗信息(所有属性可不填&#xff0c;会有默认值) 1.title:"", //标题 2.content:"", //内容 3.confirmBoxCo…

使用phpStudy小皮面板模拟后端服务器,搭建H5网站运行生产环境

一.下载安装小皮 小皮面板官网下载网址&#xff1a;小皮面板(phpstudy) - 让天下没有难配的服务器环境&#xff01; 安装说明&#xff08;特别注意&#xff09; 1. 安装路径不能包含“中文”或者“空格”&#xff0c;否则会报错&#xff08;例如错误提示&#xff1a;Cant cha…

Qwen2.5-7B大模型微调记录

Qwen2.5-7B大模型微调记录 研究需要&#xff0c;需要搞一个大模型出来&#xff0c;没有太多的时间自己训练&#xff0c;准备用现成的开源大模型&#xff0c;然后结合研究方向进行微调 前前后后折腾大半个月&#xff0c;总算做完了第一个微调的大模型&#xff0c;模型基于阿里…

VUE_使用el.animate实现自定义指令抖动效果

// 在 Vue 2 中注册自定义指令 Vue.directive(shake,{// 当被绑定的元素插入到 DOM 中时inserted(el, binding){let value binding.valueconsole.log(el, binding)// 设置 transform-origin 样式el.style.transformOrigin center bottom;const keyframes [{ transform: rota…