【设计模式】【创建型模式(Creational Patterns)】之建造者模式(Builder Pattern)

news2024/11/27 6:12:23

建造者模式(Builder Pattern) 是一种创建型设计模式,它允许你逐步构造复杂对象,而不必使用一个庞大的构造函数。建造者模式的主要目的是将对象的构建过程与其表示分离,从而使得相同的构建过程可以创建不同的表示。

主要角色
  1. Builder(抽象建造者):定义创建一个产品对象的各个部件的接口。
  2. ConcreteBuilder(具体建造者):实现 Builder 接口,构建和装配各个部件,最终构造出产品对象。
  3. Director(导演类):负责调用具体的建造者来构建产品的各个部件。
  4. Product(产品类):表示被构造的复杂对象,包含多个部件。

2. UML 类图及解释

UML 类图
+----------------+                +---------------------+
|   Director     |                | ConcreteBuilder     |
|----------------|                |---------------------|
| - builder: Builder              | - product: Product  |
|                                |                     |
| + construct(): void            | + buildPartA(): void|
| + setBuilder(builder: Builder):| + buildPartB(): void|
|   void                            | + getResult(): Product |
+----------------+                +---------------------+
        |                                  ^
        |                                  |
        |                                  |
        v                                  |
+----------------+                         |
|   Builder      |                         |
|----------------|                         |
| - product: Product                       |
|                                +---------+
| + buildPartA(): void                    |
| + buildPartB(): void                    |
| + getResult(): Product                  |
+----------------+                        |
                                           
                                           
+----------------+                         |
|   Product      |                         |
|----------------|                         |
| - partA: string                           |
| - partB: string                           |
|                                +---------+
| + setPartA(partA: string): void          |
| + setPartB(partB: string): void          |
+----------------+                         |
类图解释
  • Director:负责调用具体的建造者来构建产品的各个部件。它不依赖于具体的产品类,而是依赖于抽象建造者。
  • Builder:定义了一个创建产品对象的接口,但不具体实现。具体实现由具体的建造者类完成。
  • ConcreteBuilder:实现了 Builder 接口,负责构建和装配各个部件,最终构造出产品对象。
  • Product:表示被构造的复杂对象,包含多个部件。具体的建造者类会逐步构建这个对象。

3. 代码案例及逻辑详解

Java 代码案例
// 产品类
class Product {
    private String partA;
    private String partB;

    public void setPartA(String partA) {
        this.partA = partA;
    }

    public void setPartB(String partB) {
        this.partB = partB;
    }

    @Override
    public String toString() {
        return "Product [partA=" + partA + ", partB=" + partB + "]";
    }
}

// 抽象建造者
interface Builder {
    void buildPartA();
    void buildPartB();
    Product getResult();
}

// 具体建造者
class ConcreteBuilder implements Builder {
    private Product product;

    public ConcreteBuilder() {
        this.product = new Product();
    }

    @Override
    public void buildPartA() {
        product.setPartA("Part A");
    }

    @Override
    public void buildPartB() {
        product.setPartB("Part B");
    }

    @Override
    public Product getResult() {
        return product;
    }
}

// 导演类
class Director {
    private Builder builder;

    public void setBuilder(Builder builder) {
        this.builder = builder;
    }

    public void construct() {
        builder.buildPartA();
        builder.buildPartB();
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Director director = new Director();
        ConcreteBuilder builder = new ConcreteBuilder();

        director.setBuilder(builder);
        director.construct();

        Product product = builder.getResult();
        System.out.println(product);
    }
}
C++ 代码案例
#include <iostream>
#include <string>

// 产品类
class Product {
public:
    std::string partA;
    std::string partB;

    void setPartA(const std::string& partA) {
        this->partA = partA;
    }

    void setPartB(const std::string& partB) {
        this->partB = partB;
    }

    friend std::ostream& operator<<(std::ostream& os, const Product& p) {
        return os << "Product [partA=" << p.partA << ", partB=" << p.partB << "]";
    }
};

// 抽象建造者
class Builder {
public:
    virtual void buildPartA() = 0;
    virtual void buildPartB() = 0;
    virtual Product* getResult() = 0;
};

// 具体建造者
class ConcreteBuilder : public Builder {
private:
    Product* product;

public:
    ConcreteBuilder() {
        product = new Product();
    }

    ~ConcreteBuilder() {
        delete product;
    }

    void buildPartA() override {
        product->setPartA("Part A");
    }

    void buildPartB() override {
        product->setPartB("Part B");
    }

    Product* getResult() override {
        return product;
    }
};

// 导演类
class Director {
private:
    Builder* builder;

public:
    void setBuilder(Builder* builder) {
        this->builder = builder;
    }

    void construct() {
        builder->buildPartA();
        builder->buildPartB();
    }
};

// 客户端代码
int main() {
    Director director;
    ConcreteBuilder builder;

    director.setBuilder(&builder);
    director.construct();

    Product* product = builder.getResult();
    std::cout << *product << std::endl;

    return 0;
}
Python 代码案例
# 产品类
class Product:
    def __init__(self):
        self.partA = None
        self.partB = None

    def set_part_a(self, partA):
        self.partA = partA

    def set_part_b(self, partB):
        self.partB = partB

    def __str__(self):
        return f"Product [partA={self.partA}, partB={self.partB}]"

# 抽象建造者
class Builder:
    def build_part_a(self):
        pass

    def build_part_b(self):
        pass

    def get_result(self):
        pass

# 具体建造者
class ConcreteBuilder(Builder):
    def __init__(self):
        self.product = Product()

    def build_part_a(self):
        self.product.set_part_a("Part A")

    def build_part_b(self):
        self.product.set_part_b("Part B")

    def get_result(self):
        return self.product

# 导演类
class Director:
    def __init__(self):
        self.builder = None

    def set_builder(self, builder):
        self.builder = builder

    def construct(self):
        self.builder.build_part_a()
        self.builder.build_part_b()

# 客户端代码
if __name__ == "__main__":
    director = Director()
    builder = ConcreteBuilder()

    director.set_builder(builder)
    director.construct()

    product = builder.get_result()
    print(product)

 

Go 代码案例
package main

import "fmt"

// 产品类
type Product struct {
    PartA string
    PartB string
}

func (p *Product) SetPartA(partA string) {
    p.PartA = partA
}

func (p *Product) SetPartB(partB string) {
    p.PartB = partB
}

func (p *Product) String() string {
    return fmt.Sprintf("Product [partA=%s, partB=%s]", p.PartA, p.PartB)
}

// 抽象建造者
type Builder interface {
    BuildPartA()
    BuildPartB()
    GetResult() *Product
}

// 具体建造者
type ConcreteBuilder struct {
    product *Product
}

func NewConcreteBuilder() *ConcreteBuilder {
    return &ConcreteBuilder{product: &Product{}}
}

func (b *ConcreteBuilder) BuildPartA() {
    b.product.SetPartA("Part A")
}

func (b *ConcreteBuilder) BuildPartB() {
    b.product.SetPartB("Part B")
}

func (b *ConcreteBuilder) GetResult() *Product {
    return b.product
}

// 导演类
type Director struct {
    builder Builder
}

func (d *Director) SetBuilder(builder Builder) {
    d.builder = builder
}

func (d *Director) Construct() {
    d.builder.BuildPartA()
    d.builder.BuildPartB()
}

// 客户端代码
func main() {
    director := &Director{}
    builder := NewConcreteBuilder()

    director.SetBuilder(builder)
    director.Construct()

    product := builder.GetResult()
    fmt.Println(product)
}

4. 总结

建造者模式 是一种非常有用的创建型设计模式,尤其适用于构建复杂的对象。通过将对象的构建过程与其表示分离,建造者模式使得相同的构建过程可以创建不同的表示。这种模式的主要优点包括:

  1. 封装性:将复杂的构建过程封装在建造者类中,客户端无需知道具体的构建细节。
  2. 灵活性:可以通过不同的建造者类创建不同类型的产品对象,增加了系统的灵活性。
  3. 可扩展性:增加新的建造者类时,无需修改现有代码,符合开闭原则。

然而,建造者模式也有一些缺点,例如代码量会增加,且在简单对象的构建中可能显得过于复杂。因此,选择是否使用建造者模式应根据具体的需求和场景来决定。

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

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

相关文章

VisionPro 机器视觉案例 之 凹点检测

第十六篇 机器视觉案例 之 凹点检测 文章目录 第十六篇 机器视觉案例 之 凹点检测1.案例要求2.实现思路2.1 方式一&#xff1a;斑点工具加画线工具加点线距离工具2.2 方法二 使用斑点工具的结果集边缘坐标的横坐标最大值ImageBoundMaxX2.3 方法三 使用斑点工具的结果集凹点结果…

Java ArrayList 与顺序表:在编程海洋中把握数据结构的关键之锚

我的个人主页 我的专栏&#xff1a;Java-数据结构&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01;点赞❤ 收藏❤ 前言&#xff1a;在 Java编程的广袤世界里&#xff0c;数据结构犹如精巧的建筑蓝图&#xff0c;决定着程序在数据处理与存储时的效率、灵活性以…

【k8s】资源限制管理:Namespace、Deployment与Pod的实践

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Kubernetes航线图&#xff1a;从船长到K8s掌舵者》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 目录 一、引言 1、什么是k8s 2、在k8s使用资源配额的作…

lua除法bug

故事背景&#xff0c;新来了一个数值&#xff0c;要改公式。神奇的一幕出现了&#xff0c;公式算出一个非常大的数。排查是lua有一个除法bug,1除以大数得到一个非常大的数。 function div(a, b)return tonumber(string.format("%.2f", a/b)) end print(1/73003) pri…

微信小程序学习指南从入门到精通

&#x1f5fd;微信小程序学习指南从入门到精通&#x1f5fd; &#x1f51d;微信小程序学习指南从入门到精通&#x1f51d;✍前言✍&#x1f4bb;微信小程序学习指南前言&#x1f4bb;一、&#x1f680;文章列表&#x1f680;二、&#x1f52f;教程文章的好处&#x1f52f;1. ✅…

《基于FPGA的便携式PWM方波信号发生器》论文分析(三)——数码管稳定显示与系统调试

一、论文概述 基于FPGA的便携式PWM方波信号发生器是一篇由任青颖、庹忠曜、黄洵桢、李智禺和张贤宇 等人发表的一篇期刊论文。该论文主要研究了一种新型的信号发生器&#xff0c;旨在解决传统PWM信号发生器在移动设备信号调控中存在的精准度低和便携性差的问题 。其基于现场可编…

计算机操作系统——进程控制(Linux)

进程控制 进程创建fork&#xff08;&#xff09;函数fork() 的基本功能fork() 的基本语法fork() 的工作原理fork() 的典型使用示例fork() 的常见问题fork() 和 exec() 结合使用总结 进程终止与$进程终止的本质进程终止的情况正常退出&#xff08;Exit&#xff09;由于信号终止非…

【贪心算法第四弹——376.摆动序列】

目录 1.题目解析 题目来源 测试用例 2.算法原理 3.实战代码 代码解析 本题还可以使用动态规划的解法来解决&#xff0c;不过动态规划的时间复杂度为O(N^2)&#xff0c;而贪心解法的时间复杂度为O(N)&#xff0c;动态规划方法的博客链接: 动态规划-子序列问题——376.摆动…

我谈离散傅里叶变换的补零

有限序列的零延拓——零延拓不会改变离散傅里叶变换的形状的续篇。 L点序列可以做N点傅里叶变换&#xff0c;当 L ⩽ N L\leqslant N L⩽N时不会产生混叠。这部分内容在Rafael Gonzalez和Richard Woods所著的《数字图像处理》完全没有提到。 补零是序列末尾补零&#xff0c;不…

day18 结构体

有参宏和函数的区别 1.展开时机&#xff1a;有参宏而言&#xff0c;在预处理阶段展开&#xff0c;而函数在调用时才展开 2.内存使用&#xff1a;有参宏而言&#xff0c;占用的是所在函数的空间&#xff0c;而函数在调用时会单独开辟空间 3.效率上&#xff1a;有参宏的效率比…

C嘎嘎探索篇:栈与队列的交响:C++中的结构艺术

C嘎嘎探索篇&#xff1a;栈与队列的交响&#xff1a;C中的结构艺术 前言&#xff1a; 小编在之前刚完成了C中栈和队列&#xff08;stack和queue&#xff09;的讲解&#xff0c;忘记的小伙伴可以去我上一篇文章看一眼的&#xff0c;今天小编将会带领大家吹奏栈和队列的交响&am…

Postman设置接口关联,实现参数化

&#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 postman设置接口关联 在实际的接口测试中&#xff0c;后一个接口经常需要用到前一个接口返回的结果&#xff0c; 从而让后一个接口能正常执行&#xff0c;这…

大模型的RAG微调与Agent:提升智能代理的效率与效果

目录 ​编辑 引言 RAG模型概述 检索阶段 生成阶段 RAG模型的微调 数据集选择 损失函数设计 微调策略 超参数调整 RAG模型在智能代理中的应用 客户服务 信息检索 内容创作 决策支持&#xff1a; 结论 引言 在人工智能的快速发展中&#xff0c;大型预训练模型&a…

前端---CSS(部分用法)

HTML画页面--》这个页面就是页面上需要的元素罗列起来&#xff0c;但是页面效果很差&#xff0c;不好看&#xff0c;为了让页面好看&#xff0c;为了修饰页面---》CSS CSS的作用&#xff1a;修饰HTML页面 用了CSS之后&#xff0c;样式和元素本身做到了分离的效果。---》降低了代…

【R语言管理】Pycharm配置R语言及使用Anaconda管理R语言虚拟环境

目录 使用Anaconda创建R语言虚拟环境1. 安装Anaconda2. 创建R语言虚拟环境 Pycharm配置R语言1. 安装Pycharm2. R Language for IntelliJ插件 参考 使用Anaconda创建R语言虚拟环境 1. 安装Anaconda Anaconda的安装可参见另一博客-【Python环境管理工具】Anaconda安装及使用教程…

互联网视频推拉流EasyDSS视频直播点播平台视频转码有哪些技术特点和应用?

视频转码本质上是一个先解码再编码的过程。在转码过程中&#xff0c;原始视频码流首先被解码成原始图像数据&#xff0c;然后再根据目标编码标准、分辨率、帧率、码率等参数重新进行编码。这样&#xff0c;转换前后的码流可能遵循相同的视频编码标准&#xff0c;也可能不遵循。…

Linux Shell 脚本题目集

1、执行 ping 命令对指定主机进行测试&#xff0c;以确定该主机是否处于存活状态并输出相应结果。 #!/bin/bashread -p "请输入主机号&#xff1a;" pc # 读取用户输入的主机号if [ -z "$pc" ];then # 检查用户输入是否为空echo "主…

使用ENSP实现默认路由

一、项目拓扑 二、项目实现 1.路由器AR1配置 进入系统试图 sys将路由器命名为R1 sysname R1关闭信息中心 undo info-center enable 进入g0/0/0接口 int g0/0/0将g0/0/0接口IP地址配置为2.2.2.1/24 ip address 2.2.2.1 24进入g0/0/1接口 int g0/0/1将g0/0/1接口IP地址配置为1.…

【vue3实现微信小程序】每日专题与分页跳转的初步实现

快速跳转&#xff1a; 我的个人博客主页&#x1f449;&#xff1a;Reuuse博客 新开专栏&#x1f449;&#xff1a;Vue3专栏 参考文献&#x1f449;&#xff1a;uniapp官网 免费图标&#x1f449;&#xff1a;阿里巴巴矢量图标库 ❀ 感谢支持&#xff01;☀ 前情提要 &#x…

小程序-基于java+SpringBoot+Vue的网上花店微信小程序设计与实现

项目运行 1.运行环境&#xff1a;最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。 2.IDE环境&#xff1a;IDEA&#xff0c;Eclipse,Myeclipse都可以。推荐IDEA; 3.tomcat环境&#xff1a;Tomcat 7.x,8.x,9.x版本均可 4.硬件环境&#xff1a…