面向对象设计模式--构建器模式(C++可执行代码)

构建器模式

(Builder)

        构建器 是一种创建型设计模式,使你能够分步骤创建复杂对象。该模式允许你使用相同的创建 代码生成不同类型和形式的对象。

1. 问题 

        假设有这样一个复杂对象,在对其进行构造时需要对诸多成员变量和嵌套对象进行繁复的初始化工作。这些初始化代码通常深藏于一个包含众多参数且让人基本看不懂的构造函数中;甚至还有更糟糕的情况,那就是这些代码散落在客户端代码的多个位置。

如果为每种可能的对象都创建一个子类,这可能会导致程序变得过于复杂。

        例如,我们来思考如何创建一个 房屋 House 对象。建造一栋简单的房屋,首先你需要建造四面墙和地板,安装房门和一套窗户,然后再建造一个屋顶。但是如果你想要一栋更宽敞更明亮的房屋,还要有院子和其他设施(例如暖气、排水和供电设备),那又该怎么办呢?

拥有大量输入参数的构造函数也有缺陷:这些参数也不是每次都要全部用上的。

        最简单的方法是扩展 房屋 基类,然后创建一系列涵盖所有参数组合的子类。但最终你将面对相当数量的子类。任何新增的参数(例如门廊类型)都会让这个层次结构更加复杂。

        另一种方法则无需生成子类。你可以在 房屋 基类中创建一个包括所有可能参数的超级构造函数,并用它来控制房屋对象。这种方法确实可以避免生成子类,但它却会造成另外一个问题。

        通常情况下,绝大部分的参数都没有使用,这使得对于构造函数的调用十分不简洁。例如,只有很少的房子有游泳池,因此与游泳池相关的参数十之八九是毫无用处的。

2. 解决方案

        构建器模式建议将对象构造代码从产品类中抽取出来,并将其放在一个名为构建器的独立对象中。

构建器模式让你能够分步骤创建复杂对象。构建器不允许其他对象访问正在创建中的产品。

        该模式会将对象构造过程划分为一组步骤,比如 buildWalls 创建墙壁 和 buildDoor 创建房门 创建房门等。每次创建对象时,你都需要通过构建器对象执行一系列步骤。重点在于你无需调用所有步骤,而只需调用创建特定对象配置所需的那些步骤即可。

        当你需要创建不同形式的产品时,其中的一些构造步骤可能需要不同的实现。例如,木屋的房门可能需要使用木头制造,而城堡的房门则必须使用石头制造。

        在这种情况下,你可以创建多个不同的构建器,用不同方式实现一组相同的创建步骤。然后你就可以在创建过程中使用这些构建器(例如按顺序调用多个构造步骤)来生成不同类型的对象。

        例如,假设第一个建造者使用木头和玻璃制造房屋,第二个建造者使用石头和钢铁, 而第三个建造者使用黄金和钻石。在调用同一组步骤后, 第一个建造者会给你一栋普通房屋,

        第二个会给你一座小城堡,而第三个则会给你一座宫殿。但是,只有在调用构造步骤的客户端代码可以通过通用接口与建造者进行交互时,这样的调用才能返回需要的房屋。

主管

        你可以进一步将用于创建产品的一系列构建器步骤调用抽取成为单独的主管类。主管类可定义创建步骤的执行顺序,而构建器则提供这些步骤的实现。

        严格来说,你的程序中并不一定需要主管类。客户端代码可直接以特定顺序调用创建步骤。不过,主管类中非常适合放入各种例行构造流程,以便在程序中反复使用。

        此外,对于客户端代码来说,主管类完全隐藏了产品构造细节。客户端只需要将一个构建器与主管类关联,然后使用主管类来构造产品,就能从构建器处获得构造结果了。

3. 结构

构建器模式的结构

其中:

        *Builder为创建一个Product对象的各个部件指定抽象接口。

        *Concrete Builder实现Builder的接口以构造和装配该产品的各个部件,定义并明确它所创建的表示,提供一个检索产品的接口。

        * Director构造一个使用Builder接口的对象。

        *Product表示被构造的复杂对象。Concrete Builder创建该产品的内部表示并定义它的装配过程。包含定义组成组件的类,包括将这些组件装配成最终产品的接口。

4. 实现方式

1. 清晰地定义通用步骤,确保它们可以制造所有形式的产品。

        否则你将无法进一步实施该模式。

2. 在基本构建器接口中声明这些步骤。

3. 为每个形式的产品创建具体构建器类,并实现其构造步骤。

        不要忘记实现获取构造结果对象的方法。你不能在构建器接口中声明该方法,因为不同构建器构造的产品可能没有公共接口,因此你就不知道该方法返回的对象类型。但是,如果所有产品都位于单一类层次中,你就可以安全地在基本接口中添加获取生成对象的方法。

4. 考虑创建主管类。

        它可以使用同一构建器对象来封装多种构造产品的方式。

5. 客户端代码会同时创建构建器和主管对象。

        构造开始前,客户端必须将构建器对象传递给主管对象。通常情况下,客户端只需调用主管类构造函数一次即可。主管类使用构建器对象完成后续所有制造任务。还有另一种方式,那就是客户端可以将构建器对象直接传递给主管类的制造方法。

6. 只有在所有产品都遵循相同接口的情况下,构造结果可以直接通过主管类获取。

        否则,客户端应当通过构建器获取构造结果。

5. 代码示例

builder.h

#ifndef DESIGN_PATTERNS_BUILDER_H
#define DESIGN_PATTERNS_BUILDER_H
//------------------------------//
class Pen {};//绘笔
class Graphics {};//图样
//------------------------------//
class PersonBuilder //"人物构建器"类
{
public:
	PersonBuilder() {};
	virtual ~PersonBuilder() {};
	PersonBuilder(Pen*, Graphics*);
	virtual void BuildHead() {};//绘制头部
	virtual void BuildBody() {};//绘制身体

protected:
	Pen* pen;
	Graphics* graphics;
};
//------------------------------//
class PersonThinBuilder : public PersonBuilder //"瘦子构建器"类
{
public:
	PersonThinBuilder(Pen*, Graphics*);
	void BuildHead();
	void BuildBody();
};

class PersonFatBuilder : public PersonBuilder //"胖子构建器"类
{
public:
	PersonFatBuilder(Pen*, Graphics*);
	void BuildHead();
	void BuildBody();
};
//------------------------------//
class PersonDirector //"总构建器"类
{
public:
	PersonDirector(PersonBuilder*);
	void CreatePerson();
private:
	PersonBuilder* person_builder;

};
//------------------------------//
#endif //DESIGN_PATTERNS_BUILDER_H

builder.c

#include <iostream>
#include "builder.h"
using namespace std;
//------------------------------//
PersonDirector::PersonDirector(PersonBuilder* person_builder) : person_builder(person_builder) {}

void PersonDirector::CreatePerson() 
{
	person_builder->BuildBody();
	person_builder->BuildHead();
}
//------------------------------//
PersonBuilder::PersonBuilder(Pen* pen, Graphics* graphics) : pen(pen), graphics(graphics){}
//------------------------------//
PersonThinBuilder::PersonThinBuilder(Pen* pen, Graphics* graphics) : PersonBuilder(pen, graphics) {}

void PersonThinBuilder::BuildHead() 
{
	cout << "构建瘦的头部" << endl;
}

void PersonThinBuilder::BuildBody() 
{
	cout << "构建瘦的身体" << endl;
}
//------------------------------//
PersonFatBuilder::PersonFatBuilder(Pen* pen, Graphics* graphics) : PersonBuilder(pen, graphics) {}

void PersonFatBuilder::BuildHead() 
{
	cout << "构建胖的头部" << endl;
}
void PersonFatBuilder::BuildBody() 
{
	cout << "构建胖的身体" << endl;
}
//------------------------------//

Main.c

//------------------------------//
#include <iostream>
#include "builder.h"
using namespace std;
//------------------------------//
// Created by Cls on 2024/03/28.
//------------------------------//
int main(int argc, char *argv[])
{
	Pen* pen;
	Graphics* graphics;
	PersonBuilder* person_builder;
	PersonDirector* person_director;
	//-----------------//
	pen = new Pen;
	graphics = new Graphics;
	//-----------------//
	// 构建瘦角色
	person_builder = new PersonThinBuilder(pen, graphics);
	person_director = new PersonDirector(person_builder);
	person_director->CreatePerson();
	cout << "------------------" << endl;
	//-----------------//
	// 构建胖角色
	person_builder = new PersonFatBuilder(pen, graphics); 
	person_director = new PersonDirector(person_builder);
	person_director->CreatePerson();
	cout << "------------------" << endl;
	//-----------------//
	delete pen;
	delete graphics;
	delete person_builder;
	delete person_director;
	//-----------------//
	return 0;
}
//------------------------------//

打印输出

6. 应用场景

使用构建器模式可避免“重叠构造函数(telescopic  constructor)”的出现。

        假设你的构造函数中有十个可选参数,那么调用该函数会非常不方便;因此,你需要重载这个构造函数,新建几个只有较少参数的简化版。 但这些构造函数仍需调用主构造函数,传递一些默认数值来替代省略掉的参数。

        构建器模式让你可以分步骤生成对象,而且允许你仅使用必须的步骤。应用该模式后,你再也不需要将几十个参数塞进构造函数里了。

当你希望使用代码创建不同形式的产品(例如石头或木头房屋)时,可使用构建器模式。

        如果你需要创建的各种形式的产品,它们的制造过程相似且仅有细节上的差异,此时可使用构建器模式。

        基本构建器接口中定义了所有可能的制造步骤,具体构建器将实现这些步骤来制造特定形式的产品。同时,主管类将负责管理制造步骤的顺序。

使用构建器构造组合树或其他复杂对象。

        构建器模式让你能分步骤构造产品。你可以延迟执行某些步骤而不会影响最终产品。你甚至可以递归调用这些步骤,这在创建对象树时非常方便。

        构建器在执行制造步骤时,不能对外发布未完成的产品。这可以避免客户端代码获取到不完整结果对象的情况。

7. 优缺点

        √ 你可以分步创建对象,暂缓创建步骤或递归运行创建步骤。

        √ 生成不同形式的产品时,你可以复用相同的制造代码。

        √ 单一职责原则。你可以将复杂构造代码从产品的业务逻辑中分离出来。

        × 由于该模式需要新增多个类,因此代码整体复杂程度会有所增加。

8. 与其他模式的关系

        • 在许多设计工作的初期都会使用工厂方法(较为简单,而且可以更方便地通过子类进行定制),随后演化为使用抽象工厂、原型或构建器(更灵活但更加复杂)。

        • 构建器重点关注如何分步生成复杂对象。抽象工厂专门用于生产一系列相关对象。抽象工厂会马上返回产品,构建器则允许你在获取产品前执行一些额外构造步骤。

        • 你可以在创建复杂组合树时使用构建器,因为这可使其构造步骤以递归的方式运行。

        • 你可以结合使用构建器和桥接模式: 主管类负责抽象工作,各种不同的构建器负责实现工作。

        • 抽象工厂、构建器和原型都可以用单例来实现。

  • 16
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值