【再谈设计模式】建造者模式~对象构建的指挥家

一、引言

         在软件开发的世界里,创建对象是一项基本且频繁的操作。然而,当对象的构造变得复杂,涉及众多属性和初始化步骤时,传统的构造函数方式往往会让代码陷入混乱的泥沼。就如同搭建一座复杂的建筑,若没有合理的规划和组织,各个部件随意堆砌,最终的结果可能是摇摇欲坠。在这种情况下,建造者设计模式就如同一位经验丰富的建筑师,为我们提供了一种有条不紊地构建复杂对象的方法,它将构建过程与对象的最终表示分离开来,使我们能够轻松应对复杂对象创建的挑战。

二、定义与描述

        建造者设计模式属于创建型设计模式,它将一个复杂对象的构建过程与它的表示分离,使得同样的构建过程可以创建不同的表示。这种模式允许你使用相同的构建代码创建不同类型和配置的对象。

三、抽象背景

        在软件开发中,经常会遇到创建复杂对象的情况。如果直接使用构造函数或者简单的工厂方法来创建对象,可能会导致构造函数参数过多,代码难以维护和理解。例如,创建一个包含多个属性(如房屋的面积、房间数量、装修风格等)的对象时,直接构造可能会变得非常复杂。

四、适用场景与现实问题

解决复杂对象创

        当创建的对象具有多个可选参数或者复杂的初始化过程时,建造者模式可以清晰地分离构建步骤,使代码更易读和维护。
        例如,在创建一个汽车对象时,汽车可能有不同的颜色、型号、配置(如自动或手动档)等属性。使用建造者模式可以方便地根据用户需求组合这些属性来创建汽车对象。


对象创建过程的定制化
        如果需要根据不同的条件或需求定制对象的创建过程,建造者模式很有用。
比如在构建一份个性化的餐食订单,根据顾客的不同口味选择(主食、配菜、饮料等)构建不同的餐食对象。

五、现实生活的例子

以建造房屋为例。
        建造一座房子是一个复杂的过程,包括打地基、砌墙、盖屋顶等步骤。
有不同类型的房子,如别墅、公寓等,它们的建造过程基本相同,但最终呈现的样子(表示)不同。


        建筑工人(建造者)按照建筑师(导演者)的要求,通过相同的基本建造步骤(打地基、砌墙、盖屋顶等),但使用不同的材料或者设计风格,建造出不同类型的房子。

六、初衷与问题解决

初衷
        为了应对复杂对象的创建,将对象的构建过程从对象本身的表示中分离出来,提高代码的可维护性和可扩展性。
问题解决
        解决了构造函数参数过多的问题。例如,原本创建一个复杂对象可能需要一个包含十几个参数的构造函数,使用建造者模式后,可以通过多个简单的设置方法来逐步构建对象。
便于对象的定制化创建,满足不同用户或场景的需求。

七、代码示例

Java示例

// 产品类:房屋
class House {private String foundation;private String walls;private String roof;public House(String foundation, String walls, String roof) {this.foundation = foundation;this.walls = walls;this.roof = roof;}public String toString() {return "House with foundation: " + foundation + ", walls: " + walls + ", roof: " + roof;}
}// 抽象建造者
abstract class HouseBuilder {protected House house;public House getHouse() {return house;}public abstract void buildFoundation();public abstract void buildWalls();public abstract void buildRoof();
}// 具体建造者:别墅建造者
class VillaBuilder extends HouseBuilder {public VillaBuilder() {house = new House("", "", "");}@Overridepublic void buildFoundation() {house.foundation = "Deep and large foundation for villa";}@Overridepublic void buildWalls() {house.walls = "High - quality stone walls for villa";}@Overridepublic void buildRoof() {house.roof = "Sloping roof for villa";}
}// 导演者
class HouseDirector {public House constructHouse(HouseBuilder builder) {builder.buildFoundation();builder.buildWalls();builder.buildRoof();return builder.getHouse();}
}// 测试
public class BuilderPatternJava {public static void main(String[] args) {HouseDirector director = new HouseDirector();HouseBuilder villaBuilder = new VillaBuilder();House villa = director.constructHouse(villaBuilder);System.out.println(villa);}
}

C++示例

#include <iostream>
#include <string>// 产品类:汽车
class Car {
private:std::string color;std::string model;std::string gearType;public:Car(std::string color, std::string model, std::string gearType) : color(color), model(model), gearType(gearType) {}friend std::ostream& operator<<(std::ostream& os, const Car& car) {os << "Car - Color: " + car.color + ", Model: " + car.model + ", Gear Type: " + car.gearType;return os;}
}// 抽象建造者
class CarBuilder {
protected:Car* car;public:Car* getCar() {return car;}virtual void buildColor() = 0;virtual void buildModel() = 0;virtual void buildGearType() = 0;
}// 具体建造者:跑车建造者
class SportsCarBuilder : public CarBuilder {
public:SportsCarBuilder() {car = new Car("", "", "");}void buildColor() override {car->color = "Red";}void buildModel() override {car->model = "Sports Model";}void buildGearType() override {car->gearType = "Manual";}
}// 导演者
class CarDirector {
public:Car* constructCar(CarBuilder* builder) {builder->buildColor();builder->buildModel();builder->buildGearType();return builder->getCar();}
}// 测试
int main() {CarDirector director;CarBuilder* sportsCarBuilder = new SportsCarBuilder();Car* sportsCar = director.constructCar(sportsCarBuilder);std::cout << *sportsCar << std::endl;delete sportsCarBuilder;delete sportsCar;return 0;
}

Python示例

# 产品类:披萨
class Pizza:def __init__(self):self.dough = Noneself.sauce = Noneself.toppings = []def __str__(self):topping_str = ', '.join(self.toppings)return f"Pizza with {self.dough} dough, {self.sauce} sauce and {topping_str} toppings"# 抽象建造者
class PizzaBuilder:def __init__(self):self.pizza = Pizza()def get_pizza(self):return self.pizzadef build_dough(self):passdef build_sauce(self):passdef add_toppings(self):pass# 具体建造者:夏威夷披萨建造者
class HawaiianPizzaBuilder(PizzaBuilder):def build_dough(self):self.pizza.dough = "Thin"def build_sauce(self):self.pizza.sauce = "Tomato"def add_toppings(self):self.pizza.toppings = ["Ham", "Pineapple"]# 导演者
class PizzaDirector:def construct_pizza(self, builder):builder.build_dough();builder.build_sauce();builder.add_toppings();return builder.get_pizza()# 测试
if __name__ == "__main__":director = PizzaDirector()builder = HawaiianPizzaBuilder()pizza = director.construct_pizza(builder)print(pizza)

Go示例

// 产品类:电脑
type Computer struct {cpu    stringmemory stringstorage string
}func (c Computer) String() string {return "Computer with CPU: " + c.cpu + ", Memory: " + c.memory + ", Storage: " + c.storage
}// 抽象建造者
type ComputerBuilder interface {BuildCPU()BuildMemory()BuildStorage()GetComputer() Computer
}// 具体建造者:游戏电脑建造者
type GamingComputerBuilder struct {computer Computer
}func (g *GamingComputerBuilder) BuildCPU() {g.computer.cpu = "High - performance CPU"
}func (g *GamingComputerBuilder) BuildMemory() {g.computer.memory = "Large - capacity memory"
}func (g *GamingComputerBuilder) BuildStorage() {g.computer.storage = "High - speed storage"
}func (g *GamingComputerBuilder) GetComputer() Computer {return g.computer
}// 导演者
type ComputerDirector struct{}func (d ComputerDirector) ConstructComputer(builder ComputerBuilder) Computer {builder.BuildCPU()builder.BuildMemory()builder.BuildStorage()return builder.GetComputer()
}// 测试
func main() {director := ComputerDirector{}builder := &GamingComputerBuilder{}computer := director.ConstructComputer(builder)println(computer)
}

八、建造者设计模式的优缺点

优点

解耦对象的构建和表示
        使得构建过程和最终对象的表示可以独立变化,便于代码的维护和扩展。
易于创建复杂对象
        可以分步骤构建对象,代码结构清晰,易于理解和编写。
代码复用性高
        可以复用相同的构建步骤来创建不同类型的对象。

缺点

增加代码复杂度
        需要定义多个类(建造者、导演者、产品类等),对于简单的对象创建可能会使代码过于复杂。
可能存在多余的建造者类
        如果对象的构建方式比较固定,可能不需要那么多不同的建造者类,会造成代码冗余。

九、建造者设计模式的升级版

链式建造者模式(Fluent Builder Pattern)

        在传统建造者模式的基础上,将建造者方法的返回值设置为建造者本身,这样可以实现链式调用。
        例如在Java中,可以将建造者方法返回 this。
        优点是使代码更加紧凑和易于阅读,特别是在构建对象时可以在一行中完成多个属性的设置。
        缺点是如果构建过程中出现错误,调试可能会比较复杂,因为链式调用可能会隐藏一些中间状态。
        建造者设计模式在处理复杂对象创建方面有着独特的优势,通过合理的运用可以提高代码的质量和可维护性,并且在需要定制化创建对象的场景下非常实用。

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

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

相关文章

Rust:GUI 开源框架

Rust的GUI代码包有多个选择&#xff0c;每个都有其独特的特点和优势。以下是一些比较受欢迎的Rust GUI库&#xff0c;以及它们的主要特点和适用场景&#xff1a; KAS GUI&#xff1a; 特点&#xff1a;基于Rust语言开发的状态化图形用户界面&#xff08;GUI&#xff09;框架&am…

三、模板与配置(下)

三、模板与配置 8、WXSS模板样式-全局样式和局部样式 类型说明适用情景注意点全局样式定义在 app.wxss 中的样式&#xff0c;作用于每一个页面。当有一些通用的样式规则需要应用于整个小程序时&#xff0c;比如全局的字体大小、颜色、布局等。全局样式可能会被局部样式覆盖&a…

【Leetcode热题100】C++ 题解

目录 哈希 两数之和 字母异位词分组 最长连续序列 双指针 移动零 盛最多水的容器 三数之和 接雨水 滑动窗口 无重复字符的最长子串 找到字符串中所有字母异位词 子串 和为k的子数组 滑动窗口最大值 最小覆盖子串 普通数组 最大子数组和 合并区间 轮转数组 …

SQL面试题——抖音SQL面试题 主播播出时长

主播播出时长 现有如下数据,主播id、房间号、播出的批次号,每个批次号进出房间的时间戳、分区时间: 每一次直播都有一个上播和下播,每个房间里,同一个批次号会有两条数据,分别记录了上播和下播时间,求每个主播的播出时长? 通过上面的数据,可以清晰的看出,同一个批次…

大语言模型LLM综述

一、LM主要发展阶段 1.1、统计语言模型SLM 基于统计学习方法&#xff0c;基本思想是基于马尔可夫假设HMM建立词概率预测模型。如n-gram语言模型 1.2、神经语言模型NLM 基于神经网络来做词的分布式表示。如word2vec模型 1.3、 预训练语言模型PLM 预训练一个网络模型来做词表…

用 Python 从零开始创建神经网络(七):梯度下降(Gradient Descent)/导数(Derivatives)

梯度下降&#xff08;Gradient Descent&#xff09;/导数&#xff08;Derivatives&#xff09; 引言1. 参数对输出的影响2. 斜率&#xff08;The Slope&#xff09;3. 数值导数&#xff08;The Numerical Derivative&#xff09;4. 解析导数&#xff08;The Analytical Derivat…

防爆手机市场“百花齐放”,该怎么选?

在危险作业场景&#xff0c;如石化煤矿&#xff0c;通讯设备采购关系到提高生产效率‌&#xff0c;保障安全生产‌&#xff0c;‌符合法规要求‌。在应急救援通信场景&#xff0c;通讯设备采购&#xff0c;与保障救援行动的效率和准确性息息相关。而通信设备的性能参数是评估其…

似然函数解析

从贝叶斯定理引出似然函数 最大似然估计用来估计均值方差的 文心对似然函数的解释&#xff0c;注意 抛出正反的概率&#xff0c;就是固定均值方差的条件概率密度了

记录配置ubuntu18.04下运行ORBSLAM3的ros接口的过程及执行单目imu模式遇到的问题(详细说明防止忘记)

今天的工作需要自己录制的数据集来验证昨天的标定结果 用ORBSLAM3单目imu模式运行&#xff0c;mentor给的是一个rosbag格式的数据包&#xff0c;配置过程出了几个问题记录一下&#xff0c;沿配置流程写。 一.orbslam3编译安装 1.首先是安装各种依赖 这里不再赘述&#xff0…

机器学习中的两种主要思路:数据驱动与模型驱动

在机器学习的研究和应用中&#xff0c;如何从数据中提取有价值的信息并做出准确预测&#xff0c;是推动该领域发展的核心问题之一。在这个过程中&#xff0c;机器学习方法主要依赖于两种主要的思路&#xff1a;数据驱动与模型驱动。这两种思路在不同的应用场景中发挥着至关重要…

实验5:网络设备发现、管理和维护

实验5&#xff1a;网络设备发现、管理和维护 实验目的及要求&#xff1a; 通过实验&#xff0c;掌握Cisco 路由器和交换机的IOS配置管理。自动从NTP服务器获取时间信息。能够利用TFTP服务器实现路由器和交换机配置文件的备份和恢复。同时验证CDP协议和LLDP协议的网络参数。完…

MongoDB创建只读用户并授权指定集合的查询权限

MongoDB创建只读用户并授权指定集合的查询权限 创建测试数据 use testdb db.test_t.insertOne({id:1,name:zhangsan}); db.test_t.insertOne({id:2,name:lisi}); db.test_t1.insertOne({id:1,name:zhangsan}); db.test_t1.insertOne({id:2,name:lisi}); db.test_t2.insertOne…

前端(2)——快速入门CSS

参考&#xff1a; 罗大富 CSS 参考手册 | 菜鸟教程 CSS 参考手册 1. CSS CSS全名是层叠样式表&#xff0c;中文名层叠样式表。用于定义网页样式和布局的样式表语言。 通过 CSS&#xff0c;你可以指定页面中各个元素的颜色、字体、大小、间距、边框、背景等样式&#xff0c;…

yolo标签自动标注(使用python和yolo方法)

yolo代码自动标注 1.引言1.初阶“自动标注”&#xff0c;给每个图像都生成一个固定的标注文件&#xff0c;进而在labglimg中对矩形框进行微调&#xff0c;减少标注的工作量2.高阶自动标注&#xff0c;利用我们训练好的&#xff08;但是没有特别精准的&#xff09;yolo文件先对每…

jmeter常用配置元件介绍总结之线程组

系列文章目录 安装jmeter jmeter常用配置元件介绍总结之线程组 1.线程组(用户)1.1线程组1.1.setUp线程组和tearDown线程组1.2.Open Model Thread Group(开放模型线程组)1.3.bzm - Arrivals Thread Group(到达线程组)1.4.jpgc - Ultimate Thread Group(终极线程组)1.5.jpgc - St…

spring gateway 动态路由

##yml配置 spring:application:name: public-gateway # cloud: # gateway: # routes: # - id: mybatis-plus-test # 路由的唯一标识 # uri: http://192.168.3.188:9898 # 目标服务的地址 # predicates: # - Path/test/** # 匹配…

HTTP 1.0、HTTP 1.1 和 HTTP 2.0 区别

HTTP 1.0、HTTP 1.1 和 HTTP 2.0 是超文本传输协议&#xff08;HTTP&#xff09;不同版本的规范&#xff0c;各自进行了多项更新和改进&#xff1a; 1. HTTP/1.0 单一请求-响应&#xff1a;每次请求都需要建立一个新的 TCP 连接&#xff0c;完成后立即断开。无状态连接&#…

NVT新能德科技入职测评SHL题库更新:数字推理+演绎推理高分答案、真题解析

新能德的入职Verify测评主要考察应聘者的逻辑推理能力、数学能力、数据分析能力以及处理信息的能力。根据搜索结果&#xff0c;测评通常包含以下几个部分&#xff1a; 1. **语言理解**&#xff1a;这部分包括阅读理解、逻辑填空和语句排序。要求应聘者在17分钟内完成30题&#…

AndroidStudio 获取 Git 提交次数和编译时间

build.gradle文件 1.定义获取git提交次数的代码 def getGitCommitCount() {def process new ProcessBuilder(git,rev-list, --count, HEAD).directory(project.rootDir).redirectErrorStream(true).start()def output process.inputStream.text.trim()process.waitFor()ret…

学法减分交管12123模拟练习小程序源码前端和后端和搭建教程

交管推出个学法减分&#xff0c;每个驾驶员可以把被扣的6分&#xff0c;以看视频答题的形式学习回来&#xff0c;然后答题这个一共二十道题每道题60秒&#xff0c;有好多人不会&#xff0c;用咱们的小程序就可以模拟练习强化练习&#xff0c;还有拍照识别题目找到正确答案&…