建造者模式简述及示例

概述建造者模式的结构

  1. 建造者(Builder)

    • 每个建造者负责构建一个对象的某一部分,或者在某些情况下,负责构建整个对象。每个建造者可以有自己特定的逻辑,用于设置不同的参数和属性。这种方式让每个建造者关注于其特定的部分,有助于逻辑的简化和清晰化。

  2. 产品(Product)

    • 最终构建出的复杂对象(如汽车)就是产品。产品可能有许多属性和状态,这些属性可以通过不同的建造者来设置。

  3. 指挥者(Director)

    • 指挥者的角色是协调不同的建造者,按照一定的顺序调用它们的方法,以完成复杂对象的构建。指挥者提供了一个高层的接口,客户端不需要关心具体的构建步骤,只需调用指挥者的相关方法。

工作流程

  • 客户端通过指挥者请求构建一个复杂对象。指挥者会选择一个合适的建造者。

  • 指挥者控制建造过程,逐步调用建造者的方法来构建对象的不同部分。

  • 最后,指挥者返回完整的构建好的对象。

优势

  • 清晰的责任分离:构建步骤和具体实现分开,职责明确。

  • 灵活性和可扩展性:可以方便地添加新的建造者来扩展构建新类型的对象。

  • 代码易于维护:构建过程和构建细节分散,使得代码的可读性好、逻辑更简洁。

建造者模式实际上通过模块化构建过程,确保了每个部分的构建都能有序进行,同时将复杂对象的构建过程进行简化和清晰化。你可以将其视作一种工厂模式的变体,但着重于逐步构建复杂对象,而不是一次性创建。这样一来,整个系统的可维护性、可读性和扩展性都得到了显著提升。

我的例子

首先放一下类图:  

                                (笔者第一次画类图,有问题和错误请指出)

#include <iostream>
#include <string.h>

using namespace std;

/*原始基类,套餐的结构*/
class Meal
{
public:
    // 获取饮料
    string getDrink()
    {
        return drink;
    }
    // 获取汉堡
    string getBurger()
    {
        return burger;
    }
    // 获取薯条
    string getFries()
    {
        return fries;
    }
    // 设置饮料
    void setDrink(const string& drink)
    {
        this->drink = drink;
    }
    // 设置汉堡
    void setBurger(const string& burger)
    {
        this->burger = burger;
    }
    // 设置薯条
    void setFries(const string& fries)
    {
        this->fries = fries;
    }
    // 重载输出流运算符,格式化输出套餐内容
    friend ostream& operator<<(ostream& os, const Meal& that)
    {
        os << "套餐内容:" << endl;
        os << "     " << that.drink << endl;
        os << "     " << that.burger << endl;
        os << "     " << that.fries << endl;
        return os;
    }
private:
    string burger; // 汉堡
    string fries;  // 薯条
    string drink;  // 饮料
};

/*抽象父类,抽象建造者*/
class MealBuilder
{
protected:
    Meal* meal; // 套餐指针
public:
    MealBuilder() {}
    // 建造饮料的纯虚函数
    virtual void builderDrink() = 0;
    // 建造汉堡的纯虚函数
    virtual void builderBurger() = 0;
    // 建造薯条的纯虚函数
    virtual void builderFries() = 0;
    // 创建套餐
    void createMeal()
    {
        meal = new Meal();
    }
    // 获取套餐
    Meal* getMeal()
    {
        return meal;
    }

};

// 鸡肉套餐建造者
class ChickenMealBuilder : public MealBuilder
{
public:
    ChickenMealBuilder() {}
    // 建造鸡肉套餐饮料
    void builderDrink()override
    {
        meal->setDrink("可口可乐");
    }
    // 建造鸡肉堡
    void builderBurger()override
    {
        meal->setBurger("鸡肉堡");
    }
    // 建造特大份薯条
    void builderFries() override
    {
        meal->setFries("特大份薯条");
    }
};

// 牛肉套餐建造者
class BeefBurgerBuilder :public MealBuilder
{
public:
    BeefBurgerBuilder() {}
    // 建造芬达饮料
    void builderDrink() override
    {
        meal->setDrink("芬达");
    }
    // 建造牛肉汉堡
    void builderBurger() override
    {
        meal->setBurger("牛肉汉堡");
    }
    // 建造薯块
    void builderFries() override
    {
        meal->setFries("薯块");
    }
};

// 虾肉套餐建造者
class ShrimpMealBuilder :public MealBuilder
{
public:
    ShrimpMealBuilder() {}
    // 建造果粒橙饮料
    void builderDrink() override
    {
        meal->setDrink("果粒橙");
    }
    // 建造虾肉汉堡
    void builderBurger() override
    {
        meal->setBurger("虾肉汉堡");
    }
    // 建造虾条
    void builderFries() override
    {
        meal->setFries("虾条");
    }
};

// 套餐构建导演
class MealDirector
{
private:
    MealBuilder* mealBuilder; // 套餐建造者指针
public:
    MealDirector(MealBuilder* builder) :mealBuilder(builder) {}
    // 构建套餐
    Meal* construct()
    {
        mealBuilder->createMeal();
        mealBuilder->builderDrink();
        mealBuilder->builderBurger();
        mealBuilder->builderFries();
        return mealBuilder->getMeal();
    }
};

int main(int argc, const char* argv[])
{
    /*牛肉套餐*/
    MealBuilder* beefBuilder = new BeefBurgerBuilder();
    MealDirector beefDirector(beefBuilder);
    Meal* beef = beefDirector.construct();
    cout << *beef << endl;

    /*鸡肉套餐*/
    MealBuilder* chickenBuilder = new ChickenMealBuilder();
    MealDirector chickenDirector(chickenBuilder);
    Meal* chicken = chickenDirector.construct();
    cout << *chicken << endl;

    /*虾肉套餐*/
    MealBuilder* shrimpBuilder = new ShrimpMealBuilder();
    MealDirector shrimpDirector(shrimpBuilder);
    Meal* shrimp = shrimpDirector.construct();
    cout << *shrimp << endl;

    // 释放资源
    delete beefBuilder;
    delete chickenBuilder;
    delete shrimpBuilder;

    return 0;
}

例子介绍

这个例子实现了一个用餐套餐构建系统,使用了建造者模式,整体设计清晰且结构合理。但是非常粗糙,仅供参考理解建造者模式,下面是一些我自己总结的缺点和改进建议:

缺点:
  1. 动态内存管理:目前在 MealBuilder 中通过 new 动态分配的 Meal 对象没有得到适当地释放,这可能会导致内存泄漏。在 main 中没有delete相应的 Meal 对象。

  2. 缺少智能指针:使用原始指针(如 Meal* meal)较难管理生命周期,容易导致内存泄漏或悬挂指针。建议使用 std::unique_ptrstd::shared_ptr 来更安全地管理内存。

  3. 构建过程的灵活性不足:当前,每个套餐的构建过程是固定的,不能自由组合不同的食材。如果需要更灵活的组合,建造者模式将显得有些不适合。

  4. 缺少析构函数:如果使用了 raw pointer,建议在使用 MealBuilder 时添加析构函数以确保资源的释放,或将其变成一个智能指针以确保自动释放。

改进建议:
  1. 内存管理的改进

    • Meal* meal; 改为 std::unique_ptr<Meal> meal;,这样可以自动管理内存。

    • MealDirector 的析构函数中也可以添加删除逻辑,确保不留内存泄漏。

  2. 构建过程的灵活性

    • 可以考虑设计一个更通用的接口,使得在构建套餐时支持自由组合。例如,增加添加食材的方法,使得套餐的每个部分可以单独选择。

  3. 输入参数处理

    • 可以考虑以参数的形式传递套餐的组成部分,从而提升灵活性并减少成员函数数量。

  4. 使用现代C++特性

    • 使用 std::arraystd::vector 来管理多项套餐,使得管理多个产品组件更为高效。

    • 加强使用栈而非堆来管理简单对象(如 Meal),然后在构造结束时将其返回以减少不必要的动态分配。

应用场景

建造者模式在许多实际应用场景中非常有效,特别是在需要构建复杂对象的情况下。以下是一些具体的应用场景:

1. 餐厅点餐系统

在餐厅点餐时,顾客可以根据自己的需求逐步选择饮料、主菜和配菜。建造者模式能够将这个过程分为不同的建造者类,使得每种套餐的构建都非常清晰。

2. 文档生成

在需要生成复杂文档(如报告或邮件)时,可以使用建造者模式。有些文档可能由标题、摘要、正文和附录组成,使用不同的建造者可以控制各个组成部分的格式和内容。

3. 图形用户界面(GUI)

在构建用户界面时,不同的界面元素(如按钮、文本框和菜单)可以通过不同的建造者进行组合。每个建造者负责生成特定类型的界面组件,这样可以灵活地组合和重用。

4. 配置文件生成

在创建复杂的配置文件或对象时,建造者模式可以提供清晰的构建步骤。用户可以通过选择不同的参数和选项来灵活构造配置,而不必关注其底层实现。

5. 游戏角色创建

在游戏开发中,角色创建通常涉及多个属性(如健康值、攻击力、技能等)的组合。每个角色的建造者可以负责定义一组属性和外观,从而灵活构建不同类型的角色。

6. 数据库查询构建

在处理复杂的数据库查询时,建造者模式可以用来逐步组合查询的不同部分(如选择字段、过滤条件、排序等),从而生成最终的 SQL 查询字符串。

7. 复杂对象的配置

例如,在软件配置或安装程序中,需要指定多个选项和参数(如服务器设置、安装路径、必要的依赖项等),建造者模式能够提供用户友好的配置过程。

8. 网络请求构建

在发起复杂的网络请求时,建造者模式可以用来逐步设置请求的各个方面,如请求方法、URL、请求头和请求体。这种方式可以提高请求构建的可读性和易用性

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值