C# 设计模式之创建者模式

news2025/1/12 12:03:07

总目录


前言

在软件系统中,有时需要创建一个复杂对象,并且这个复杂对象由其各部分子对象通过一定的步骤组合而成。例如一个采购系统中,如果需要采购员去采购一批电脑时,在这个实际需求中,电脑就是一个复杂的对象,它是由CPU、主板、硬盘、显卡、机箱等组装而成的,如果此时让采购员一台一台电脑去组装的话真是要累死采购员了,这里就可以采用建造者模式来解决这个问题,我们可以把电脑的各个组件的组装过程封装到一个建造者类对象里,建造者只要负责返还给客户端全部组件都建造完毕的产品对象就可以了。然而现实生活中也是如此的,如果公司要采购一批电脑,此时采购员不可能自己去买各个组件并把它们组织起来,此时采购员只需要像电脑城的老板说自己要采购什么样的电脑就可以了,电脑城老板自然会把组装好的电脑送到公司。下面就以这个例子来展开建造者模式的介绍。


1 基本介绍

  1. 建造者模式(Builder Pattern),将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。说白了就是将一个复杂的对象拆分成一个一个零件,然后按照既定顺序和规则进行组装,最终形成这个相对复杂的对象。

  2. 建造者模式的本质是使组装过程(用指挥者类进行封装,从而达到解耦的目的)和创建具体产品解耦,使我们不用去关心每个组件是如何组装的。

  3. 建造者模式中的4个角色

    • Product(产品):复杂对象本身。
    • Builder(抽象建造者):既可以是抽象类也可以是接口,主要是为了约束和规范具体建造者有哪些建造行为,并提供一个方法返回组装后的复杂对象。
    • ConcreteBuilder(具体建造者):它继承自Builder(抽象建造者),主要是具体实现父类中的那些建造行为。也就是说在这个类里就要实际去创建各个零件的具体功能了。
    • Director(指挥者):又称为导演类,在指挥者中不涉及具体产品的信息,只负责保证对象各部分完整创建或按某种顺序创建。客户端一般只需要与指挥者进行交互,在客户端确定具体建造者的类型,并实例化具体建造者对象,然后通过指挥者类的构造函数或者Setter方法将该对象传入指挥者类中。

2 使用场景

有时需要创建一个复杂对象,其通常由其各部分子对象通过一定的步骤组合而成。

建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,并不适合使用建造者模式。

本质就是:创建的对象较复杂,由多个部件构成,各部件面临着复杂的变化,但构件间的建造顺序是稳定的。使得相同的创建过程可以创建不同的产品。

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

垃圾食品套餐系统:汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出各种特惠"套餐"。
装修系统:改水电、再贴瓷砖、家电、电视墙等,顺序基本不变,用材不同最终生成的产品则不同。

3 实现方式

在这个例子中,电脑城的老板是直接与客户(也就是指采购员)联系的,然而电脑的组装是由老板指挥装机人员去把电脑的各个部件(CPU,风扇…等)组装起来,真正负责创建产品(这里产品指的就是电脑)的人就是电脑城的装机人员。

  • Computer是产品,包含CPU、主板、内存、硬盘、显卡、机箱等组件。
  • Builder是抽象建造者,
  • XiaoWangBuilder和XiaoZhangBuilder是具体的建造者,也就是装机人员
  • Director是指挥者,也就是电脑城的老板。

1 首先要明确,产品类以及产品类的组件:

	//产品类
    public class Computer
    {
        public string CPU { get; set; }
        public string Memory { get; set; }
        //其他的组件 省略
        public string Disk { get; set; }
    }

2 再者,抽象的建造类,将建造者的建造行为抽象出来了,并提供一个方法返回组装后复杂产品

    //抽象建造者类
    public abstract class AbstactComputerBuilder
    {
        protected Computer computer = new Computer();

        //以下方法定义了产品不同组件的建造行为
        public abstract void BuildCPU(string cpu);
        public abstract void BuildMemory(string Memory);
        public abstract void BuildDisk(string disk);

        //定义一个方法返回产品实例
        public Computer GetComputer()
        {
            Console.WriteLine("电脑组装完成!");
            return computer;
        }
    }

3 然后,是具体的建造者类,负责具体的建造工作

	//装机小哥 张三
	public class ZhangSanBuilder : AbstactComputerBuilder
    {
        public override void BuildCPU(string cpu)
        {
            computer.CPU = cpu;
            Console.WriteLine($"张三已将CPU:{computer.CPU}组装完成");
        }

        public override void BuildDisk(string disk)
        {
            computer.Disk =disk;
            Console.WriteLine($"张三已将硬盘:{computer.Disk}组装完成");
        }

        public override void BuildMemory(string memory)
        {
            computer.Memory = memory;
            Console.WriteLine($"张三已将内存:{computer.Memory}组装完成");
        }
    }

	//装机小哥 李四
    public class LiSiBuilder : AbstactComputerBuilder
    {
        public override void BuildCPU(string cpu)
        {
            computer.CPU = cpu;
            Console.WriteLine($"李四已将CPU:{computer.CPU}组装完成");
        }

        public override void BuildDisk(string disk)
        {
            computer.Disk = disk;
            Console.WriteLine($"李四已将硬盘:{computer.Disk}组装完成");
        }

        public override void BuildMemory(string memory)
        {
            computer.Memory = memory;
            Console.WriteLine($"李四已将内存:{computer.Memory}组装完成");
        }
    }

4 指挥者,指挥装机小哥装机并制定和产品对象的建造顺序

    public class Director
    {
        private AbstactComputerBuilder _builder = null;

        //通过构造函数传递具体创造者
        public Director(AbstactComputerBuilder builder)
        {
            this._builder = builder;
        }

        // 组装方法,并返回产品
        public Computer AssembleComputer(string cpu,string disk,string memory)
        {
            _builder.BuildCPU(cpu);
            _builder.BuildDisk(disk);
            _builder.BuildMemory(memory);
            return _builder.GetComputer();
        }
    }

在上述代码中是通过构造函数传递具体建造者,也可以在AssembleComputer方法中传递。产品的具体组装顺序则是由AssembleComputer方法来完成,如果有多种组装方式,也可以有多个方法来分别完成。该类本质就是统筹安排,并直接与客户端进行交互。

5 最后,客户端的调用

        public static void Main(string[] args)
        {
            Console.WriteLine("我想组装一台工作电脑:");
            //首先实例化一个具体建造者对象:装机小哥 张三
            AbstactComputerBuilder zhangSan = new ZhangSanBuilder();
            //然后使用指挥者类来具体组装这个产品
            Computer workComputer = new Director(zhangSan).AssembleComputer("I5","500G","6G");

            Console.WriteLine("\n我又想组装一台游戏电脑:");
            //实例化一个具体建造者对象:装机小哥 李四
            AbstactComputerBuilder liSi = new LiSiBuilder();
            //然后使用指挥者类来具体组装这个产品
            Computer gameComputer = new Director(liSi).AssembleComputer("I7","2T","16G");

            Console.ReadLine();
        }

在这里插入图片描述
6 从上面我们发现,指挥者主要的作用就是制定组装的流程,如果我们去掉将制定的过程交给建造者抽象类呢

    //抽象建造者类
    public abstract class AbstactComputerBuilder
    {
        protected Computer computer = new Computer();

        //以下方法定义了产品不同组件的建造行为
        public abstract void BuildCPU(string cpu);
        public abstract void BuildMemory(string Memory);
        public abstract void BuildDisk(string disk);

        // 组装方法,并返回产品
        public Computer AssembleComputer(string cpu, string disk, string memory)
        {
            this.BuildCPU(cpu);
            this.BuildDisk(disk);
            this.BuildMemory(memory);
            return this.GetComputer();
        }

        //定义一个方法返回产品实例
        public Computer GetComputer()
        {
            Console.WriteLine("电脑组装完成!");
            return computer;
        }
    }

此时的客户端调用:

        public static void Main(string[] args)
        {
            Console.WriteLine("我想组装一台工作电脑:");
            //首先实例化一个具体建造者对象:装机小哥 张三
            AbstactComputerBuilder zhangSan = new ZhangSanBuilder();
            //然后使用指挥者类来具体组装这个产品
            Computer workComputer = zhangSan.AssembleComputer("I5","500G","6G");

            Console.WriteLine("\n我又想组装一台游戏电脑:");
            //实例化一个具体建造者对象:装机小哥 李四
            AbstactComputerBuilder liSi = new LiSiBuilder();
            //然后使用指挥者类来具体组装这个产品
            Computer gameComputer = liSi.AssembleComputer("I7","2T","16G");

            Console.ReadLine();
        }

4 相关分析

介绍完了建造者模式的具体实现之后,让我们总结下建造模式的实现要点:
在建造者模式中,指挥者是直接与客户端打交道的,指挥者将客户端创建产品的请求划分为对各个部件的建造请求,再将这些请求委派到具体建造者角色,具体建造者角色是完成具体产品的构建工作的,却不为客户所知道。

建造者模式主要用于“分步骤来构建一个复杂的对象”,其中“分步骤”是一个固定的组合过程,而复杂对象的各个部分是经常变化的(也就是说电脑的内部组件是经常变化的,这里指的的变化如硬盘的大小变了,CPU由单核变双核等)。

产品不需要抽象类,由于建造模式的创建出来的最终产品可能差异很大,所以不大可能提炼出一个抽象产品类。

由于建造者隐藏了具体产品的组装过程,所以要改变一个产品的内部表示,只需要再实现一个具体的建造者就可以了,从而能很好地应对产品组成组件的需求变化。

5 创建者模式与其他工厂模式的对比

  • 工厂模式解决了“同一类产品”的需求变化,抽象工厂模式解决了“系列产品”的需求变化,而建造者模式解决的是 “产品部分” 的需要变化。
  • 工厂方法模式 VS 建造者模式
    • 工厂方法模式侧重整体对象的创建方式,建造者模式侧重零部件的构建,然后通过一定顺序和规则构造出一个复杂的对象。例如:想要制作一个假人,如果使用工厂方法模式,直接生产出来一个XX材质、XX高、XX重的假人人就可以了。而如果使用建造者模式,则需要先创建出四肢、头和躯干等部位,然后按照一定顺序进行组装形成一个完整的假人。
  • 抽象工厂模式 VS 建造者模式
    • 抽象工厂模式侧重对产品簇(系列产品)的创建,一个产品簇是这样的一系列产品。
    • 采用抽象工厂模式不需要关心构建过程,只关心什么产品由什么工厂生产即可。
    • 建造者模式则侧重要求按照指定的蓝图建造产品,它的主要目的是通过组装零配件而产生一个新产品。如果将抽象工厂模式看成汽车配件生产工厂,生产一个产品族的产品,那么建造者模式就是一个汽车组装工厂,通过对部件的组装可以返回一辆完整的汽车。

结语

希望以上内容可以帮助到大家,如文中有不对之处,还请批评指正。


参考资料:
c#建造者模式详解

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

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

相关文章

DVWA (SQL注入 low)

(1)判断注入是字符型还是数值型 判断恒为真,字符型,输出ures表中全部用户信息。 1 or 123 123 # (2)猜测SQL查询字段数 1后面加 闭合 id 查询的 ,末尾加# 是注释了后面 的内容&#xff…

LLM智能体工程落地思考(一)

人工智能领域著名教授吴恩达在今年3月份红杉资本的人工智能峰会(AI Ascent)以及最近Snowflake峰会开发者日上都发表了关于AI Agent(人工智能体)的演讲。演讲中,其分享了对AI Agent未来发展潜力的展望。认为AI Agent能够让人工智能胜任更多种类的任务,甚至…

STL源码刨析:树的导览

目录 1.前言 2.关联式容器 3.二叉搜索树 4.平衡二叉搜索树 前言 在阅读了STL源码刨析系列的前几章,我们可以知道容器分为序列式容器和关联式容器。在前几个小节中,我们对序列式容器中的vector和list容器进行了讲解,并比较了vector和list的区别…

学习记录——day24 多线程编程

目录 多线程局部概念 线程支持函数(多线程编程) pthread_create:创建线程 pthread -self:线程号获取 pthread_exit:线程退出函数 pthread_jion:线程资源回收 pthred_detath:线程分离态 p…

检索增强生成(RAG):智能内容生成的新纪元

引言 在大 AI 时代,生成式人工智能(GenAI)模型,尤其是大型语言模型(LLM),已经展现出了令人瞩目的能力。然而,这些模型在提供信息的准确、即时、专业、权威等方面仍存在局限。检索增…

1.2、安装k8s-node1 和 k8s-node2节点虚拟机

k8s-master节点的虚拟机环境弄好之后,这小节继续介绍k8s-node1 和 k8s-node2节点虚拟机环境安装。 节点主机名ip主节点k8s-master172.31.0.10节点1k8s-node1172.31.0.11节点2k8s-node2172.31.0.12 在D:\vagrant目录下新建centos_stream_9_node1文件夹,然…

如何在Python中使用网页抓取API获得Google搜索结果

SERP是搜索引擎结果页的缩写,它是你在百度、谷歌、Bing等搜索引擎中提交查询后所得到的页面。搜索引擎需要给所有页面做排序,把最能解决我们需求的页面展示给我们,企业会非常关注结果页的排序,也就是本企业内容的自然排名情况。手…

革新IT架构管理,宝兰德全新中间件统一管理平台助力企业数字化转型

近期,宝兰德在金融行业科技盛会“2024中国国际金融展”上正式发布了拳头产品「中间件统一管理平台MCP2.0」,旨在推动业务与中间件解耦,解决中间件管理中的版本不统一、自动化程度低、监控不完善、运维效率低、管理分散等问题,实现…

8月1日学习笔记 java环境安装以及tomcat配置

一,java环境安装 1. 效果 2. 步骤 1. 下载 jdk22 # 官网地址 https://www.oracle.com/cn/java/technologies/download s/ wget https://download.oracle.com/java/22/latest/jdk- 22_linux-x64_bin.tar.gz 2. 解压 tar -zxvf jdk-22.2.tar.gz 3. 移动到 us…

快速搞定分布式Kafka

本文从kafka中的实际应用场景分析,讲述kafka的一些基本概念。再讲述zookeeper集群环境的构建;kafka的搭建以及脚本文件编写;最后是一个快速入门的demo.内容会比较多,希望大家能有所收获! 1.Kafka(MQ)实战应用场景剖析…

linux系统ShellCheck检查shell脚步语法正确的工具

目录 ShellCheck 安装ShellCheck 、dnf、yum 源代码编译 步骤如下: 示例命令: 方法三:使用其他第三方仓库、COPR 仓库 假设 ShellCheck 输出如下: 分析输出 修改脚本 再次运行 ShellCheck 1. Shell 脚本最佳实践 主题…

vcpkg install libtorch[cuda] -allow-unsupported-compiler

在vcpkg中不懂如何使用 nvcc 的 -allow-unsupported-compiler, 所以直接注释了CUDA中对版本的检查代码. C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.8\include\crt\host_config.h 奇了怪了,我是用的是vs2022,但是还是被检查为不支持的编译器!!! 可以试一下改这…

搭建gitlab代码托管仓库(解决centos7无法使用默认数据源问题)

公司的gitlab由于停电,又崩了,每次停电都会崩掉。所以就想到自己学一下搭建gitlab代码中心,后面在搞一个jenkins自动发版学习一下,慢慢搞吧。 在弄的时候,发现Centos7居然在2024年6月31日停止维护了。这就离谱了&…

职教国培丨高职教师数据分析与挖掘课程实施能力提升培训班莅临泰迪智能科技参观调研

7月28日,由广东机电职业技术学院牵头,广东泰迪智能科技股份有限公司为合作单位的“2024年高职教师数据分析与挖掘课程实施能力提升培训班”老师莅临广东泰迪智能科技股份有限公司产教融合实训基地参观调研,来自广东省各地36位高校教师参与本次…

如何在 Kali Linux 上安装和使用 Docker 和 Docker Compose

Docker 和 Docker Compose 是现代开发者必备的工具,特别是当你需要在不同的环境中部署应用时。本文将详细介绍如何在 Kali Linux 上安装 Docker 和 Docker Compose,并使用它们启动服务。即使你是个技术小白,也能轻松跟随这篇指南完成操作。 …

Ecovadis认证:企业申请Ecovadis认证条件

Ecovadis认证是一种用于评估和评价企业可持续发展绩效的认证体系。该认证由Ecovadis公司提供,目的是帮助公司了解和改善其环境、社会和治理(ESG)实践。 Ecovadis认证主要基于四个方面进行评估:环境、劳工和人权、道德采购以及可持…

Python——记录pip问题(解决下载慢、升级失败问题)

在python开发中,经常需要使用到各种各样的库。 pip又是我们常用的安装工具。但是国外的源下载速度实在太慢,经常导致超时。 有很多朋友刚刚学Python的时候,会来问为什么pip下载东西这么慢啊? 而且pycharm里面下载库也是非常的慢…

Linux服务器安装MySQL8.0

序号类型地址1MySQLLinux(centos 7.5)服务器安装MySQL5.72MySQLLinux服务器安装MySQL8.03MySQLMySQL操作之概念、SQL约束(一)4MySQLMySQL操作之数据定义语言(DDL)(二)5MySQLMySQL操作之数据操作…

React三原理和路由

代码下载 React 组件通讯原理 setState() 说明 setState() 是异步更新数据的,使用该语法时,后面的 setState() 不要依赖于前面的 setState(),可以多次调用 setState() ,只会触发一次重新渲染: this.setState({ coun…

CPQ报价管理系统 | 成本报价CPQ解决方案

一、成本报价流程现状 1、传统流程 2、业务痛点 ①、数据手工重复输入环节多、易错,为保障准确性需多次复核,影响报价效率 ②、原材波动较大,但是当前询价流程只有一次性,原材成本发生变化,无法及时更新变化提醒报价…