1 前言
上一篇文章中对创建型设计模式之一的“工厂模式”进行概述与总结,分别描述了三种工厂模式(简单工厂、工厂方法、抽象工厂)的含义、特点、优缺点以及适用场景,并以C++语言实现具体例子。本文描述另一创建型设计模式——建造者模式。
2 什么是建造者模式
建造者(Builder)模式,或者称为生成器模式,是创建型设计模式的一种。建造者模式是将一个复杂化对象的构造过程与其产品对象本身解耦,使得同样的构建过程可以创建不同的产品。利用构建模式,复杂化的对象被分解为多个简单的对象,然后每个分步构建每个简单对象;构建模式还将不变化部分抽象出来(如产品组成),变化部分可以灵活组合。
相关文章:
设计模式回顾——原型模式
设计模式回顾——观察者模式
设计模式回顾——模板模式
设计模式回顾——策略模式
设计模式回顾——适配器模式
设计模式回顾——建造者模式
设计模式回顾——工厂模式
设计模式回顾——单例模式
设计模式回顾——设计模式概念与基本原则
2.1 建造者模式组成
建造者模式由产品(Product)、抽象建造者(Abstract Builder)、具体建造者(Concrete Builder)、指挥者(Director)、客户(Client)i五个要素组成。
-
产品(Product), 由多个简单对象构成的复杂对象,各个简单对象由构建者建造
-
抽象建造者(Abstract Builder), 声明产品各个子对象创建的抽象方法的接口,通常还提供一个返回复杂对象的方法
GetProduct()
-
具体建造者(Concrete Builder), 继承抽象建造者类,并实现抽象接口,即是实现产品各个子对象的具体创建方法
-
指挥者(Director),调用建造者的各个子对象方法完成复杂对象的创建,指挥者不涉及具体产品信息
-
客户(Client),客户和指挥者进行需求沟通,严格来说客户不属于建造者模式的一部分
2.2 建造者UML图
根据建造者模式的组成要素,以及它们之间的关系,画出建造者模式的UML图如下。
3 建造者模式优缺点
优点:
-
解耦
将产品本身与产品创建过程进行解耦,客户不必关系创建细节,可以使用相同的创建过程来得到不同的产品;符合细节依赖抽象的“依赖倒转原则”。
-
精细地控制对象的创建过程
将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程 。 -
易于拓展
增加新的具体建造者无需修改原有类库的代码,易于拓展,符合“开闭原则“。
不足:
-
不适合产品之间差异很大的场景
建造者模式一般用于创建具有较多的共同点的产品,其组成部分相似;如果产品之间的差异性很大,则不适合使用建造者模式,因此其使用范围受到一定的限制。
-
不适合产品内部变化很复杂的场景
如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大。
4 什么地方使用建造者模式
建造者模式的优点决定了其适用的场景,反过来其缺点即是其不适用的场景。建造者模式适用场景:
- 组成相似、具有较多共同特点的复杂化产品对象,或者产品类因为调用顺序不同而产生不同作用
- 分离复杂对象的创建和使用,期望相同创建过程可以创建不同的产品
- 一个对象初始化时,需要较多的参数,或者参数具有默认值
具体实例:
-
汽车,由发动机、变速箱、车轮、方向盘、座椅等组成
-
计算机,由主板、CPU、硬盘、内存、显卡、键盘、鼠标等组成
5 建造者模式实现
实例情况:
- 目标是创建一个复杂化的产品(Product)
- 这个产品由PartA、PartB、PartC三部分组成
实现过程:
- 第一步,声明产品类
/* product.h */
#ifndef _PRODUCT_H_
#define _PRODUCT_H_
#include <string>
using namespace std;
class Product{
public:
Product();
~Product();
void SetPartA(string part);
void
SetPartB(string part);
void SetPartC(string part);
void show();
private:
string m_PartA;
string m_PartB;
string m_PartC;
};
#endif
- 第二步,声明抽象建造者
/* abstract_builder.h */
#ifndef _ABSTRACT_BUILDER_H_
#define _ABSTRACT_BUILDER_H_
#include <string>
using namespace std;
class AbstractBuilder{
public:
AbstractBuilder();
virtual ~AbstractBuilder();
virtual void CreateProduct() = 0;
virtual void BuildPartA(string part) = 0;
virtual void BuildPartB(string part) = 0;
virtual void BuildPartC(string part) = 0;
virtual Product* GetProduct() = 0;
};
#endif
- 第三步,声明和构建具体建造者
/* builder.h */
#ifndef _BUILDER_H_
#define _BUILDER_H_
#include <string>
#include "product.h"
#include "abstract_builder.h"
using namespace std;
class Builder: public AbstractBuilder
{
public:
Builder();
~Builder();
void CreateProduct();
void BuildPartA(string part);
void BuildPartB(string part);
void BuildPartC(string part);
Product* GetProduct();
private:
Product* m_Product;
};
#endif
/* builder.cpp */
#include <iostream>
#include <stdio.h>
#include "builder.h"
Product::Product()
{
}
Product::~Product()
{
}
void Product::SetPartA(string param)
{
m_PartA = param;
}
void Product::SetPartB(string param)
{
m_PartB = param;
}
void Product::SetPartC(string param)
{
m_PartC = param;
}
void Product::show()
{
cout << "Output product,the component of product:" << m_PartA << " "
<< m_PartB << " " << m_PartC << endl;
}
AbstractBuilder::AbstractBuilder()
{
}
AbstractBuilder::~AbstractBuilder()
{
}
Builder::Builder():m_Product(NULL)
{
}
Builder::~Builder()
{
}
void Builder::CreateProduct()
{
cout << "Create a base product" << endl;
m_Product = new Product();
}
void Builder::BuildPartA(string part)
{
cout << "Building part A of the product" << endl;
m_Product->SetPartA(part);
}
void Builder::BuildPartB(string part)
{
cout << "Building part B of the product" << endl;
m_Product->SetPartB(part);
}
void Builder::BuildPartC(string part)
{
cout << "Building part C of the product" << endl;
m_Product->SetPartC(part);
}
Product* Builder::GetProduct()
{
return m_Product;
}
- 第四步,声明和构建指挥者
/* directot.h */
#ifndef _DIRECTOR_H_
#define _DIRECTOR_H_
#include "builder.h"
class Director
{
public:
Director(AbstractBuilder* builder);
~Director();
void Construct();
private:
AbstractBuilder* m_Builder;
};
#endif
/* director.cpp */
#include <stdio.h>
#include "director.h"
Director::Director(AbstractBuilder* builder)
{
m_Builder = builder;
}
Director::~Director()
{
}
void Director::Construct()
{
if (NULL == m_Builder)
{
return;
}
m_Builder->CreateProduct();
m_Builder->BuildPartA("PartA");
m_Builder->BuildPartB("PartB");
m_Builder->BuildPartC("PartC");
}
- 第五步,用户调用指挥者创建产品对象
/* client.cpp */
#include "director.h"
int main(int argc, char **argv)
{
AbstractBuilder *builder = new Builder();
Director *director = new Director(builder);
director->Construct();
Product* product = builder->GetProduct();
product->show();
delete builder;
delete director;
return 0;
}
执行结果:
acuity@ubuntu:/mnt/hgfs/LSW/STHB/design-mode/builder$ g++ -o client client.cpp builder.cpp director.cpp
acuity@ubuntu:/mnt/hgfs/LSW/STHB/design-mode/builder$ ./client
Create a base product
Building part A of the product
Building part B of the product
Building part C of the product
Output product,the component of product:PartA PartB PartC
6 建造者模式与抽象工厂模式的比较
创建者模式和抽象工厂模式都属于创建型设计模式,都是用于一个对象的“构建”,但两者又具有一定的不同点。
-
产品复杂度不同
建造者模式用于创建复杂化的产品对象;抽象工厂模式用于创建比较单一化的产品对象。如生产一台计算机和生产一个cpu的过程。
-
产品“产出”形态不同
建造者模式返回一个组装好的完整产品;抽象工厂模式返回一系列相关的产品,这些产品位于不同的产品等级结构,构成了一个产品族 。
-
创建步骤和过程不同
对于建造者模式,客户端不直接调用建造者的相关方法,而是通过指挥者类来指导如何生成对象,包括对象的组装过程和建造步骤,侧重于一步一步构造一个复杂对象,然后返回一个完整的对象 ;对于抽象工厂模式,客户端实例化工厂类,然后调用工厂方法获取所需产品对象。