
这一篇我将向大家讲解建造者(Builder)模式。在上一篇文章中我们主要学习了抽象工厂(Abstract Factory)模式,抽象工厂模式主要解决对不同等级结构的产品的创建工作,主要关注的是创建哪一批产品的问题,而本文所讲的建造者模式主要是解决对于一个产品如何分部创建的问题,这是对于建造者模式的最初描述。同样,这篇文章主要分为两大部分来讲解,第一部分我会对建造者模式的原型进行详细的说明,第二部分会对建造者模式如何解决具体问题进行探讨。
这一篇我将向大家讲解建造者(Builder)模式。在上一篇文章中我们主要学习了抽象工厂(Abstract Factory)模式,抽象工厂模式主要解决对不同等级结构的产品的创建工作,主要关注的是创建哪一批产品的问题,而本文所讲的建造者模式主要是解决对于一个产品如何分部创建的问题,这是对于建造者模式的最初描述。同样,这篇文章主要分为两大部分来讲解,第一部分我会对建造者模式的原型进行详细的说明,第二部分会对建造者模式如何解决具体问题进行探讨。
建造者模式的原型
描述:
在软件系统中,有时候面临一个"复杂对象"的创建工作,其通常由各个部分的子对象用一定算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合到一起的算法却相对稳定。
建造者模式是对对象的创建模式。建造者模式可以将一个产品的内部表象与产品的生成过程分隔开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象。建造者模式利用一个导演者对象和具体建造者对象一个一个地建造出所有的零件,从而建造出完整的产品对象。建造者模式将产品的结构和产品的零件建造过程对客户端隐藏起来,把对建造过程进行指挥的责任和具体建造零件的责任分隔开来,达到责任划分和封装的目的。
意图:
将一个复杂对象的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
模式原型UML:

建造者模式涉及到以下角色:
1、抽象建造者(Builder)角色:给出一个抽象接口,以规范产品对象的各个组成成分的建造。此接口中一般至少规定两个方法,一个是创建部分的方法,例如BuilderPart,另一个是返回结果的方法,例如GetProduct,以约束具体建造者实现。
2、具体建造者(ConcreteBuilder)角色:担任这个角色的是与应用程序紧密相关的一些类,它们在应用程序的调用下创建产品的实例。这个角色产品实现了抽象建造者接口,主要完成分部创建产品并提供产品对象的实例。
3、导演者(Director)角色:顾名思义,就是具体指挥使用哪个具体创造者来完成产品的创建,是创建工作的调用者。但是,导演者角色并没有产品类的具体知识,真正拥有产品类的具体知识的是具体建造者角色。
4、产品(Product)角色:产品角色就是建造中的复杂对象。一般只有对于复杂对象的创建才使用建造者模式,一个系统中会出现多于一个的产品类,而这些产品类并不一定有共同的接口,可能完全不关联,这时就需要提供多套抽象和具体的建造者来完成不一致的产品的创建或者是采用一个统一接口来标识产品,我个人推荐前者。请大家注意,这里的产品只是一个产品类,不存在继承关系,所以也就没有像抽象工厂中的那种客户端依赖抽象的说法了。
模式原型代码:

Builder pattern code in C#
// 建造者模式

using System;
using System.Collections;

namespace DesignPatterns.Creational


{

// 应用程序测试

public class MainApp

{
public static void Main()

{
// 创建导演者和具体建造者角色
Director director = new Director();

Builder b1 = new ConcreteBuilder1();
Builder b2 = new ConcreteBuilder2();

// 根据两个建造者获得产品类,注意应用程序直接使用产品类
director.Construct(b1);
Product p1 = b1.GetResult();
p1.Show();

director.Construct(b2);
Product p2 = b2.GetResult();
p2.Show();

// 等待输入
Console.Read();
}
}

// 导演者角色

class Director

{
// 导演者调用建造者分部创建,注意具体的创建顺序由导演者决定
public void Construct(Builder builder)

{
builder.BuildPartA();
builder.BuildPartB();
}
}

// 抽象建造者

abstract class Builder

{
public abstract void BuildPartA();
public abstract void BuildPartB();
public abstract Product GetResult();
}

// 具体建造者1

class ConcreteBuilder1 : Builder

{
private Product product = new Product();

public override void BuildPartA()

{
product.Add("PartA");
}

public override void BuildPartB()

{
product.Add("PartB");
}

public override Product GetResult()

{
return product;
}
}

// 具体建造者2

class ConcreteBuilder2 : Builder

{
private Product product = new Product();

public override void BuildPartA()

{
product.Add("PartX");
}

public override void BuildPartB()

{
product.Add("PartY");
}

public override Product GetResult()

{
return product;
}
}

// 产品类

class Product

{
ArrayList parts = new ArrayList();

public void Add(string part)

{
parts.Add(part);
}

public void Show()

{
Console.WriteLine("\nProduct Parts -------");
foreach (string part in parts)
Console.WriteLine(part);
}
}
}

输出结果为:
Product Parts -------
PartA
PartB
Product Parts -------
PartX
PartY
生活中的实例:
这个小例子向大家展示了一辆车是如何利用建造者模式被创建的,注意商店(这里就是导演者)是如何利用VehicleBuilders按照一定的顺序创建一辆车的。

Real world code using Abstract Factory in C#
// 建造者模式应用小例子

using System;
using System.Collections.Generic;

namespace DesignPatterns.Creational.Builder.RealWorld


{

// 主应用程序
public class MainApp

{

public static void Main()

{

VehicleBuilder builder;

// 利用具体Builder创建商店对象
Shop shop = new Shop();

// 踏板车建造者
builder = new ScooterBuilder();
shop.Construct(builder);
builder.Vehicle.Show();

// 小汽车建造者
builder = new CarBuilder();
shop.Construct(builder);
builder.Vehicle.Show();

// 摩托车建造者
builder = new MotorCycleBuilder();
shop.Construct(builder);
builder.Vehicle.Show();

// 等待用户输入
Console.ReadKey();
}
}
// 商店类,相当于导演者角色
class Shop

{

// 经过一个复杂的建造过程
public void Construct(VehicleBuilder vehicleBuilder)

{

vehicleBuilder.BuildFrame();

vehicleBuilder.BuildEngine();

vehicleBuilder.BuildWheels();

vehicleBuilder.BuildDoors();

}
}

// 车辆建造者,相当于抽象建造者角色
abstract class VehicleBuilder

{

protected Vehicle vehicle;

// 获得车辆产品实例
public Vehicle Vehicle

{

get
{ return vehicle; }
}

// 分部建造方法
public abstract void BuildFrame();

public abstract void BuildEngine();

public abstract void BuildWheels();

public abstract void BuildDoors();
}

// 摩托车建造者,相当于一个具体建造者角色
class MotorCycleBuilder : VehicleBuilder

{

public MotorCycleBuilder()

{

vehicle = new Vehicle("MotorCycle");
}

public override void BuildFrame()

{

vehicle["frame"] = "MotorCycle Frame";
}

public override void BuildEngine()

{

vehicle["engine"] = "500 cc";
}

public override void BuildWheels()

{

vehicle["wheels"] = "2";
}

public override void BuildDoors()

{

vehicle["doors"] = "0";
}
}

// 小汽车建造者,相当于一个具体建造者角色
class CarBuilder : VehicleBuilder

{

public CarBuilder()

{

vehicle = new Vehicle("Car");
}

public override void BuildFrame()

{

vehicle["frame"] = "Car Frame";
}

public override void BuildEngine()

{

vehicle["engine"] = "2500 cc";
}

public override void BuildWheels()

{

vehicle["wheels"] = "4";
}

public override void BuildDoors()

{

vehicle["doors"] = "4";
}
}

// 踏板车建造者,相当于一个具体建造者角色
class ScooterBuilder : VehicleBuilder

{

public ScooterBuilder()

{

vehicle = new Vehicle("Scooter");
}

public override void BuildFrame()

{

vehicle["frame"] = "Scooter Frame";
}

public override void BuildEngine()

{

vehicle["engine"] = "50 cc";
}

public override void BuildWheels()

{

vehicle["wheels"] = "2";
}

public override void BuildDoors()

{

vehicle["doors"] = "0";
}
}

// 车辆类,相当于产品类。注意这里的车辆类与摩托车、小汽车、踏板车等不是继承关系
class Vehicle

{

private string _vehicleType;

// 使用一个字典来保存部分
private Dictionary<string, string> _parts =
new Dictionary<string, string>();

// 构造器
public Vehicle(string vehicleType)

{

this._vehicleType = vehicleType;
}

// 索引器
public string this[string key]

{


get
{ return _parts[key]; }


set
{ _parts[key] = value; }
}

public void Show()

{

Console.WriteLine("\n---------------------------");

Console.WriteLine("Vehicle Type: {0}", _vehicleType);

Console.WriteLine(" Frame : {0}", _parts["frame"]);

Console.WriteLine(" Engine : {0}", _parts["engine"]);

Console.WriteLine(" #Wheels: {0}", _parts["wheels"]);

Console.WriteLine(" #Doors : {0}", _parts["doors"]);
}
}
}
输出结果为:
---------------------------
Vehicle Type: Scooter
Frame : Scooter Frame
Engine : none
#Wheels: 2
#Doors : 0
---------------------------
Vehicle Type: Car
Frame : Car Frame
Engine : 2500 cc
#Wheels: 4
#Doors : 4
---------------------------
Vehicle Type: MotorCycle
Frame : MotorCycle Frame
Engine : 500 cc
#Wheels: 2
#Doors : 0
什么情况下使用建造者:
1、需要生成的产品对象有复杂的内部结构。每一个内部成分本身可以是对象,也可以仅仅是一个对象的组成部分。
2、需要生成的产品对象的属性相互依赖。建造者模式可以强制实行一种分步骤进行的建造过程,因此,如果对象的一个属性必须在另一个属性被赋值之后才可以被赋值,使用建造者模式便是一个很好的设计思想。
3、在对象创建过程中会使用到系统中的其他一些对象,这些对象在产品对象的创建过程中不易得到。
实际项目举例
现在我们需要在一个在PC游戏中构建房子的程序。可以构建的房子有多种样式,但是房屋的结构是相对稳定的,每个房子都有地基,墙面,房间,门,窗,地板等,同时也存在一定的构建顺序(必须先构建地基,这也就是产品类的组成部分之间的依赖关系),房子的具体风格可能包括很多种,例如居民住房,存放货物的仓储房等。由于房子的结构稳定,变化的只是房子的组成部分,这时我们可以考虑采用建造者模式来完成具体房屋的创建工作,即创建房屋和使用房屋责任的分离,房屋的构建与其表示相分离。同样的建造过程可以创建不同的表示(居民住房,存放货物的仓储房等)。
本案例中的类与建造者模式中的角色的对应关系:
BuildingBuilder(房屋建造者)——Builder(抽象建造者)
Architect(建造师)——Director(导演者)
HousingBuilder(住房建造者)——ConcreteBuilder1(具体建造者1)
StorageBuilder(仓储房建造者)——ConcreteBuilder2(具体建造者2)
Building(房子)——Product(产品)
实现的代码如下:

Project code using Abstract Factory in C#
// 建造者模式项目应用案例

using System;
using System.Collections.Generic;

namespace DesignPatterns.Creational.Builder.Project


{

// 主应用程序
public class MainApp

{

public static void Main()

{

BuildingBuilder builder;

// 利用具体Builder创建建筑师对象
Architect architect = new Architect();

// 住房建造者
builder = new HousingBuilder();
architect.Construct(builder);
builder.Building.DisplayMe();

// 仓储建造者
builder = new StorageBuilder();
architect.Construct(builder);
builder.Building.DisplayMe();

// 等待用户输入
Console.ReadKey();
}
}
// 建筑师,相当于导演者角色
class Architect

{

// 经过一个复杂的建造过程
public void Construct(BuildingBuilder buildingBuilder)

{
buildingBuilder.BuildBase();

buildingBuilder.BuildWall();

buildingBuilder.BuildWindows();

buildingBuilder.BuildDoors();

buildingBuilder.BuildRooms();

buildingBuilder.BuildFloor();
}
}

// 房子建造者,相当于抽象建造者角色
abstract class BuildingBuilder

{

protected Building building;

// 获得房子实例
public Building Building

{

get
{ return building; }
}

// 分部建造方法
public abstract void BuildBase();

public abstract void BuildWall();

public abstract void BuildWindows();

public abstract void BuildDoors();

public abstract void BuildRooms();

public abstract void BuildFloor();
}

// 住房建造者,相当于一个具体建造者角色
class HousingBuilder : BuildingBuilder

{

public HousingBuilder()

{

building = new Building("Housing");
}

public override void BuildBase()

{

building["base"] = "Housing Base";
}

public override void BuildWall()

{

building["wall"] = "Housing Wall";
}

public override void BuildWindows()

{

building["windows"] = "2";
}

public override void BuildDoors()

{

building["doors"] = "2";
}

public override void BuildRooms()

{

building["rooms"] = "3";
}

public override void BuildFloor()

{

building["floor"] = "3*20m2";
}
}

// 仓储房建造者,相当于一个具体建造者角色
class StorageBuilder : BuildingBuilder

{

public StorageBuilder()

{

building = new Building("Storage");
}

public override void BuildBase()

{

building["base"] = "Storage Base";
}

public override void BuildWall()

{

building["wall"] = "Storage Wall";
}

public override void BuildWindows()

{

building["windows"] = "0";
}

public override void BuildDoors()

{

building["doors"] = "1";
}

public override void BuildRooms()

{

building["rooms"] = "0";
}

public override void BuildFloor()

{

building["floor"] = "1000m2";
}
}

// 房子类,相当于产品类
class Building

{

private string _buildingType;

// 使用一个字典来保存部分
private Dictionary<string, string> _parts =
new Dictionary<string, string>();

// 构造器
public Building(string buildingType)

{

this._buildingType = buildingType;
}

// 索引器
public string this[string key]

{


get
{ return _parts[key]; }


set
{ _parts[key] = value; }
}

public void DisplayMe()

{

Console.WriteLine();

Console.WriteLine(" Building Type: {0}", _buildingType);

Console.WriteLine(" Base : {0}", _parts["base"]);

Console.WriteLine(" Wall : {0}", _parts["wall"]);

Console.WriteLine(" #Windows : {0}", _parts["windows"]);

Console.WriteLine(" #Doors : {0}", _parts["doors"]);

Console.WriteLine(" #Rooms : {0}", _parts["rooms"]);

Console.WriteLine(" Floor : {0}", _parts["floor"]);
}
}
}
输出结果为:
Building Type : Housing
Base : Housing Base
Wall : Housing Wall
#Windows : 2
#Doors : 2
#Rooms : 3
Floor : 3*20m2
Building Type : Storage
Base : Storage Base
Wall : Storage Wall
#Windows : 0
#Doors : 1
#Rooms : 0
Floor : 1000m2
小结
1、Builder模式主要用于“分步骤构建一个复杂的对象”。在这其中“分步骤”是稳定的,而复杂对象的各个部分则经常变化。
2、Builder模式主要在于应对“复杂对象各个部分”的频繁需求变动。其缺点在于难以应对“分步骤构建算法”的需求变动。
3、Abstract Factory模式解决“系列对象”的需求变化,Builder模式解决“对象部分”的需求变化。Builder械通常和Composite模式组合使用。
Welcome to share our idea,thank you!欢迎分享您的想法,谢谢!
大家一起学面向对象设计模式系列 索引贴
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述