一文搞懂设计模式之建造者模式

大家好,我是晴天,我们又见面了,这周我们继续学习一文搞懂设计模式系列,本周将一起学习建造者模式(生成器模式)

本文目录

什么是建造者模式

建造者模式(也称为生成器模式)是一种创建型设计模式。

基本概念

建造者模式主要包含以下几个角色:

  1. 产品(Product): 要创建的复杂对象,它由多个部件组成。

  2. 抽象建造者(Builder): 定义了创建产品的抽象接口,包括创建产品的各个部件的方法。

  3. 具体建造者(Concrete Builder): 实现抽象建造者接口,负责实际构建产品的各个部件,通常包含一个用于获取构建完成后产品的方法。

  4. 指导者(Director): 负责使用建造者接口构建产品,通常包含一个构建方法,该方法通过调用建造者的方法按特定顺序构建产品。

  5. 客户端(Client): 使用指导者构建复杂对象的客户端。

基础关系

建造者模式的几个角色之间的关系如下:

  1. 产品(Product): 要创建的复杂对象,它由多个部件组成。产品的内部结构通常由多个部分组成,而具体的部分可以由抽象建造者和具体建造者来定义。

  2. 抽象建造者(Builder): 定义了创建产品的抽象接口,包括创建产品的各个部件的方法。抽象建造者中的方法通常对应产品的各个部分,但是这些方法通常是空的或者有默认实现。

  3. 具体建造者(Concrete Builder): 实现抽象建造者接口,负责实际构建产品的各个部件。具体建造者包含了具体的构建逻辑,它知道如何组装产品的各个部分,并负责最终返回构建好的产品。

  4. 指导者(Director): 负责使用建造者接口构建产品。指导者通常包含一个构建方法,该方法通过调用建造者的方法按照一定的顺序来构建产品。指导者知道构建的步骤和顺序,但它并不知道具体的产品是如何构建的。

  5. 客户端(Client): 客户端通过指导者来构建产品,而不需要知道产品的具体构建过程。客户端创建一个指导者,并将具体的建造者传递给指导者。然后,客户端通过调用指导者的构建方法来获取构建好的产品。

这些角色协同工作,使得构建过程与最终产品的表示相分离。客户端通过指导者来构建产品,而具体的构建过程由具体建造者完成。这种分离使得可以更灵活地改变产品的内部表示,同时客户端不必关心具体的构建细节。

现实举例

一个实际生活中的例子是制造汽车。汽车通常由多个部分组成,例如引擎、底盘、车身、轮胎等。而不同类型的汽车,如轿车、卡车、越野车等,可能具有不同的配置和特征。

例子类图

在这种情况下,建造者模式可以用于创建不同类型的汽车,而无需改变构建过程的逻辑。让我们看看这个例子的几个角色:

  1. 产品(Product): 汽车是要创建的产品,它由引擎、底盘、车身、轮胎等多个部分组成。

  2. 抽象建造者(Builder): 定义了创建汽车的抽象接口,包括创建汽车的各个部分的方法。例如,有一个抽象方法用于构建引擎、构建底盘、构建车身等。

  3. 具体建造者(Concrete Builder): 实现了抽象建造者接口,负责实际构建汽车的各个部分。比如,轿车建造者负责构建小型引擎和轻巧的车身,而卡车建造者负责构建大型引擎和坚固的底盘。

  4. 指导者(Director): 负责使用建造者接口构建汽车。指导者知道构建汽车的步骤和顺序,但并不知道具体的汽车是如何构建的。它可以接受不同类型的具体建造者,并使用它们来构建不同配置的汽车。

  5. 客户端(Client): 客户端通过创建一个指导者,并将具体建造者传递给指导者,来构建汽车。客户端只需要调用指导者的构建方法,而不需要了解汽车的具体构建过程。

通过这种方式,可以轻松地创建不同类型的汽车,而不必改变指导者的代码。每个具体建造者负责自己类型汽车的构建细节,从而实现了构建过程和产品的解耦。这种模式使得在需要新类型的汽车时,可以添加新的具体建造者,而无需修改现有的构建逻辑。

代码实现

我们以上述实例,来实现一个简单的demo

package main

import "fmt"

// 产品:汽车
type Car struct {
    Engine  string
    Chassis string
    Body    string
    Tires   string
}

// 抽象建造者接口
type Builder interface {
    BuildEngine()
    BuildChassis()
    BuildBody()
    BuildTires()
    GetCar() *Car
}

// 具体建造者:轿车
type SedanBuilder struct {
    car *Car
}

func NewSedanBuilder() *SedanBuilder {
    return &SedanBuilder{car: &Car{}}
}

func (sb *SedanBuilder) BuildEngine() {
    sb.car.Engine = "Small Engine"
}

func (sb *SedanBuilder) BuildChassis() {
    sb.car.Chassis = "Light Chassis"
}

func (sb *SedanBuilder) BuildBody() {
    sb.car.Body = "Sleek Body"
}

func (sb *SedanBuilder) BuildTires() {
    sb.car.Tires = "Standard Tires"
}

func (sb *SedanBuilder) GetCar() *Car {
    return sb.car
}

// 具体建造者:卡车
type TruckBuilder struct {
    car *Car
}

func NewTruckBuilder() *TruckBuilder {
    return &TruckBuilder{car: &Car{}}
}

func (tb *TruckBuilder) BuildEngine() {
    tb.car.Engine = "Large Engine"
}

func (tb *TruckBuilder) BuildChassis() {
    tb.car.Chassis = "Heavy Chassis"
}

func (tb *TruckBuilder) BuildBody() {
    tb.car.Body = "Robust Body"
}

func (tb *TruckBuilder) BuildTires() {
    tb.car.Tires = "Off-road Tires"
}

func (tb *TruckBuilder) GetCar() *Car {
    return tb.car
}

// 指导者
type Director struct {
    builder Builder
}

func NewDirector(builder Builder) *Director {
    return &Director{builder: builder}
}

func (d *Director) Construct() *Car {
    d.builder.BuildEngine()
    d.builder.BuildChassis()
    d.builder.BuildBody()
    d.builder.BuildTires()
    return d.builder.GetCar()
}

func main() {
    // 构建轿车
    sedanBuilder := NewSedanBuilder()
    director := NewDirector(sedanBuilder)
    sedanCar := director.Construct()
    fmt.Println("Sedan Car:", sedanCar)

    // 构建卡车
    truckBuilder := NewTruckBuilder()
    director = NewDirector(truckBuilder)
    truckCar := director.Construct()
    fmt.Println("Truck Car:", truckCar)
}

代码解释

以下是一个简化的使用 Go 语言实现建造者模式的例子,以汽车制造为例:

package main

import "fmt"

// 产品:汽车
type Car struct {
    Engine  string
    Chassis string
    Body    string
    Tires   string
}

// 抽象建造者接口
type Builder interface {
    BuildEngine()
    BuildChassis()
    BuildBody()
    BuildTires()
    GetCar() *Car
}

// 具体建造者:轿车
type SedanBuilder struct {
    car *Car
}

func NewSedanBuilder() *SedanBuilder {
    return &SedanBuilder{car: &Car{}}
}

func (sb *SedanBuilder) BuildEngine() {
    sb.car.Engine = "Small Engine"
}

func (sb *SedanBuilder) BuildChassis() {
    sb.car.Chassis = "Light Chassis"
}

func (sb *SedanBuilder) BuildBody() {
    sb.car.Body = "Sleek Body"
}

func (sb *SedanBuilder) BuildTires() {
    sb.car.Tires = "Standard Tires"
}

func (sb *SedanBuilder) GetCar() *Car {
    return sb.car
}

// 具体建造者:卡车
type TruckBuilder struct {
    car *Car
}

func NewTruckBuilder() *TruckBuilder {
    return &TruckBuilder{car: &Car{}}
}

func (tb *TruckBuilder) BuildEngine() {
    tb.car.Engine = "Large Engine"
}

func (tb *TruckBuilder) BuildChassis() {
    tb.car.Chassis = "Heavy Chassis"
}

func (tb *TruckBuilder) BuildBody() {
    tb.car.Body = "Robust Body"
}

func (tb *TruckBuilder) BuildTires() {
    tb.car.Tires = "Off-road Tires"
}

func (tb *TruckBuilder) GetCar() *Car {
    return tb.car
}

// 指导者
type Director struct {
    builder Builder
}

func NewDirector(builder Builder) *Director {
    return &Director{builder: builder}
}

func (d *Director) Construct() *Car {
    d.builder.BuildEngine()
    d.builder.BuildChassis()
    d.builder.BuildBody()
    d.builder.BuildTires()
    return d.builder.GetCar()
}

func main() {
    // 构建轿车
    sedanBuilder := NewSedanBuilder()
    director := NewDirector(sedanBuilder)
    sedanCar := director.Construct()
    fmt.Println("Sedan Car:", sedanCar)

    // 构建卡车
    truckBuilder := NewTruckBuilder()
    director = NewDirector(truckBuilder)
    truckCar := director.Construct()
    fmt.Println("Truck Car:", truckCar)
}

在这个例子中,Car 表示产品,Builder 是抽象建造者接口,SedanBuilderTruckBuilder 是具体建造者,Director 是指导者。客户端可以通过创建不同类型的具体建造者来构建不同类型的汽车。这样,可以轻松地扩展和修改汽车的构建过程,而不必改变客户端的代码。

代码优化

上述代码,无论是轿车的建造者还是卡车的建造者,都使用了相同的 Car 结构,并且直接为这个结构中的字段赋值。这可能在一些实际情况下并不是最灵活的做法,尤其是在不同类型的汽车具有不同属性时。

一个更灵活的方式是在 Car 结构中引入一个更通用的字段,例如一个 Options 字段,它可以是一个映射或其他适当的数据结构,以便每个具体建造者可以根据需要添加不同的属性。以下是相应的修改:

package main

import (
	"fmt"
)

// 产品:汽车
type Car struct {
	Options map[string]string
}

// 抽象建造者接口
type Builder interface {
	BuildEngine()
	BuildChassis()
	BuildBody()
	BuildTires()
	GetCar() *Car
}

// 具体建造者:轿车
type SedanBuilder struct {
	car *Car
}

func NewSedanBuilder() *SedanBuilder {
	return &SedanBuilder{car: &Car{Options: make(map[string]string)}}
}

func (sb *SedanBuilder) BuildEngine() {
	sb.car.Options["Engine"] = "Small Engine"
}

func (sb *SedanBuilder) BuildChassis() {
	sb.car.Options["Chassis"] = "Light Chassis"
}

func (sb *SedanBuilder) BuildBody() {
	sb.car.Options["Body"] = "Sleek Body"
}

func (sb *SedanBuilder) BuildTires() {
	sb.car.Options["Tires"] = "Standard Tires"
}

func (sb *SedanBuilder) GetCar() *Car {
	return sb.car
}

// 具体建造者:卡车
type TruckBuilder struct {
	car *Car
}

func NewTruckBuilder() *TruckBuilder {
	return &TruckBuilder{car: &Car{Options: make(map[string]string)}}
}

func (tb *TruckBuilder) BuildEngine() {
	tb.car.Options["Engine"] = "Large Engine"
}

func (tb *TruckBuilder) BuildChassis() {
	tb.car.Options["Chassis"] = "Heavy Chassis"
}

func (tb *TruckBuilder) BuildBody() {
	tb.car.Options["Body"] = "Robust Body"
}

func (tb *TruckBuilder) BuildTires() {
	tb.car.Options["Tires"] = "Off-road Tires"
}

func (tb *TruckBuilder) GetCar() *Car {
	return tb.car
}

// 指导者
type Director struct {
	builder Builder
}

func NewDirector(builder Builder) *Director {
	return &Director{builder: builder}
}

func (d *Director) Construct() *Car {
	d.builder.BuildEngine()
	d.builder.BuildChassis()
	d.builder.BuildBody()
	d.builder.BuildTires()
	return d.builder.GetCar()
}

func main() {
	// 构建轿车
	sedanBuilder := NewSedanBuilder()
	director := NewDirector(sedanBuilder)
	sedanCar := director.Construct()
	fmt.Println("Sedan Car:", sedanCar)

	// 构建卡车
	truckBuilder := NewTruckBuilder()
	director = NewDirector(truckBuilder)
	truckCar := director.Construct()
	fmt.Println("Truck Car:", truckCar)
}

适用场景

建造者模式适用于以下场景:

  1. 创建复杂对象: 当需要创建的对象具有复杂的内部结构,由多个部分组成,并且这些部分之间的组装方式有一定的规律时,可以使用建造者模式。这有助于将复杂对象的构建过程与其表示分离,使得可以更灵活地构建不同的表示。

  2. 避免构造方法的参数列表过长: 当一个类的构造方法需要传递很多参数,而且这些参数之间存在一定的关联关系时,使用建造者模式可以避免构造方法参数列表过长的问题。通过将这些参数封装到一个具体的建造者中,可以更清晰地管理和维护。

  3. 支持不同的构建顺序: 如果需要支持不同的构建顺序来构建对象,可以使用建造者模式。具体建造者可以实现不同的构建步骤,指导者根据需要调用这些步骤,以实现不同的构建顺序。

  4. 构建过程具有复杂的条件逻辑: 当构建对象的过程涉及复杂的条件逻辑,不同的条件可能导致不同的构建步骤时,建造者模式可以提供一种清晰的方式来处理这种复杂性。

建造者模式的优缺点

优点:

  1. 分离构建过程和表示: 建造者模式将一个复杂对象的构建过程与其最终表示相分离。这使得可以更容易地改变产品的内部表示,同时客户端不必关心具体的构建过程。

  2. 更好的控制构建过程: 通过使用指导者来控制构建过程,可以灵活地组合各个部分,实现不同的构建顺序和构建逻辑。这使得可以更好地控制对象的创建过程。

  3. 更容易扩展新的具体建造者: 向系统中添加新的具体建造者相对容易,不需要修改指导者的代码。这符合开闭原则,使得系统更容易扩展。

  4. 更易于改变产品的内部表示: 由于构建过程与最终产品的表示分离,可以更灵活地改变产品的内部结构,而不会对客户端产生影响。

缺点:

  1. 增加了系统的复杂性: 引入了多个角色(指导者、抽象建造者、具体建造者)和接口,从而增加了系统的复杂性。对于简单的对象,建造者模式可能显得过于繁琐。

  2. 可能会产生多余的Builder对象: 如果产品的构建过程比较简单,那么可能会存在一些不必要的具体建造者实现,增加了系统的开销。

  3. 不容易发现构建过程中的错误: 在编译时很难发现具体建造者中构建过程的错误,因为具体建造者的方法通常是在运行时调用的。这可能导致一些在构建过程中的逻辑错误只能在实际运行时被发现。

写在最后

感谢大家的阅读,晴天将继续努力,分享更多有趣且实用的主题,如有错误和纰漏,欢迎留言给予指正。 更多文章敬请关注作者个人公众号 晴天码字。 我们下期不见不散,to be continued…

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

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

相关文章

ov多域名证书可以保护几个域名

互联网上的站点大多遵循http明文传输协议传输数据,因此,网站在传输一些私人信息时很容易被劫持、篡改,在互联网日益普及的今天,信息安全显得尤为重要。SSL数字证书就是开发者用来保护网站信息安全的工具之一,它会为htt…

【源码】基于jsp+servlet+jdbc实现的学生管理系统

文章目录 系统介绍技术选型成果展示账号地址及源码获取 系统介绍 基于jspservletjdbc实现的学生管理系统分为管理员与学生两种角色,以下是权限说明 学生 查看/修改信息:查看/修改自己的用户信息 修改密码:修改自己的登录密码,…

LAMP集中式搭建+LNMP分布式搭建(新版)

LAMP搭建LNMP搭建 LAMP搭建LNMP搭建一、LAMP搭建(集中式)1、LAMP简介2、LAMP组件及作用3、编译安装Apache httpd服务4、编译安装mysqld 服务5、编译安装PHP解析环境6、安装论坛7、安装博客 二、LNMP搭建(分布式)1、LNMP工作原理2、安装nginx3、安装mysql4、安装php5、在浏览器测…

一文解读医疗评级 IT 基础设施灾备能力要求与 SmartX 超融合灾备解决方案(附用户实践与电子书)

近年来,电子病历系统应用水平分级评价(以下简称“电子病历评级”)和医院信息互联互通标准化成熟度测评(以下简称“互联互通评级”)正在成为国内医疗服务信息化建设的重要评价标准,各个省市都出台了明确的考…

效率必备神器

在这个快节奏的工作环境中,使用一些强大的工作软件来提高工作效率和组织工作流程变得异常重要。无论是个人任务管理还是团队协作,合适的工作软件都能极大地改善工作质量和生产力。让我们深入了解一些我个人强力推荐的工作软件,希望能给你带来…

年终盘点文生图的狂飙之路,2023年文生图卷到什么程度了?

目录 前言发展1月2月3月4月5月6月7月9月10月11月12月 思考与总结参考文献 前言 说到文生图,可能有些人不清楚,但要说AI绘画,就有很多人直呼: 2022可以说是AI绘图大爆发的元年。 AI绘画模型可以分为扩散模型(Diffusio…

基于SSM框架的音乐产品购物网站的设计与实现论文

目 录 目 录 I 摘 要 III ABSTRACT IV 1 绪论 1 1.1 课题背景 1 1.2 研究现状 1 1.3 研究内容 2 2 系统开发环境 3 2.1 vue技术 3 2.2 JAVA技术 3 2.3 MYSQL数据库 3 2.4 B/S结构 4 2.5 SSM框架技术 4 3 系统分析 5 3.1 可行性分析 5 3.1.1 技术可行性 5 3.1.2 操作可行性 5 3…

vscode不同代码的项目分配不同的工作区

vscode不同代码的项目分配不同的工作区 很多时候我们很多项目都需要使用vscode来进行编写代码,像我个人会拿vscode写python,linux远程写代码,前端和stm32的编辑器,这些项目都有自己的插件,如果我们启动某一个项目&…

Kafka设计原理详解

Kafka核心总控制器 (Controller) 在Kafka集群中,通常会有一个或多个broker,其中一个会被选举为控制器 (Kafka Controller),其主要职责是管理整个集群中所有分区和副本的状态。具体来说: 当某个分区的leader副本出现故障时&#…

基本的逻辑门

前言 本篇文章介绍基本的逻辑门,然后给出C语言描述 逻辑门是在集成电路上的基本组件。简单的逻辑门可由晶体管组成。这些晶体管的组合可以使代表两种信号的高低电平在通过它们之后产生高电平或者低电平的信号。高、低电平可以分别代表逻辑上的“真”与“假”或二进…

中非经济贸易工作委员会在深圳挂牌启动

12月中旬,中非经济贸易工作委员会在广东深圳举办主题为“中流砥柱•非凡湾区”的2023中非经济贸易大湾区论坛。中非经济贸易工作委员会在深圳正式挂牌启动,开创了粤港澳大湾区中非贸易高质量发展新格局。 十年“一带一路”建设硕果累累,中非经…

vue3实现本地开发使用的px转换成vw,px转换成rem方法整理

前言: 项目中如果想本地开发使用px,但是界面上线以后界面是自适应的效果,可以有多种方式来实现效果。 一、px转成vw 1、安装,安装成功后,node_modules 会新增这两个插件包 npm i postcss-px-to-viewport-8-plugin 2、新增 post…

微信小程序预览pdf,修改pdf文件名

记录微信小程序预览pdf文件,修改pdf名字安卓和ios都可用。 1.安卓和苹果的效果 2.需要用到的api 1.wx.downloadFile wx.downloadFile 下载文件资源到本地。客户端直接发起一个 HTTPS GET 请求,返回文件的本地临时路径 (本地路径),单次下载…

新能源重型卡车,2025年将达275亿美元

随着新能源汽车的推出,重型卡车市场正在经历重大变革。近年来,由于对可持续交通的需求不断增加以及向环保替代品的转变,新能源重型卡车市场的增长非常显着。本次分析将考察全球和中国新能源重型卡车市场的发展趋势。 在全球范围内&#xff…

浅谈矿山井下IT配电系统绝缘监测的应用

摘要:众所周知,能源作为经济发展的重要引擎,堪称是国民经济的命脉。采矿业一直是能源工业的重要支柱,我国的矿业生产主要来自井下开采,环境恶劣,就以煤矿为例,煤矿井下存在水、火、瓦斯、煤尘、…

Kali渗透-MSF木马免杀技术

前言 免杀技术全称为反杀毒技术 Anti-Virus 简称“免杀”,它指的是一种能使病毒木马免于被杀毒软件查杀的技术。由于免杀技术的涉猎面非常广,其中包含反汇编、逆向工程、系统漏洞等技术,内容基本上都是修改病毒、木马的内容改变特征码&#…

四大“水刊“之一,热度依旧不减,发文量破万!

期刊简介 2区计算机工程类SCI 【期刊概况】IF:4.0-5.0,JCR2区,中科院3区; 【终审周期】走期刊部系统,3-5个月左右录用; 【检索情况】SCI检索; 【WOS收录年份】2011年; 【自引率…

湘沪数字产业(上海)协同创新中心正式启动!

前言 随着全球化的加速和市场竞争的日益激烈,产业协作已成为推动科技创新和经济发展的重要手段。在创新驱动战略的推动下,全国形成了在科创核心城市建设科创中心的浪潮,旨在充分利用不同区域的产业优势,加强产业协作,…

德思特新闻 | 德思特PANORAMA天线与Robustel(鲁邦通)建立合作

来源:德思特测试测量 德思特新闻 | 德思特PANORAMA天线与Robustel(鲁邦通)建立合作 原文链接:https://mp.weixin.qq.com/s/Cn9BIMsvORHfakS6uSMw7w 欢迎关注虹科,为您提供最新资讯! 德思特新闻 德思特Pa…

Mac使用Python的tkinter显示异常解决方案

显示异常的原因&#xff1a; macOS版本>12 & python版本<3.9.8 参考文档 :https://www.python.org/download/mac/tcltk/ 如果python版本过低&#xff1a; brew upgrade python: 升级python版本 brew install python-tk: 安装最新的tk版本 python -m tkinter : 弹…