C++设计模式:构建器模式(九)

1、定义与动机
  • 定义:将一个复杂对象的构建与其表示相分离,使得同样的构建过程(稳定)可以创建不同的表示(变化)

  • 动机:

    • 在软件系统中,有时候面临着“一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构建而成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定
    • 如何应对这种变化?如何提供一种“封装机制”来隔离出“复杂对象的各个部分”的变化,从而保持系统中的“稳定构建算法”不随着需求改变而改变?
  • 个人理解:构建器模式严格来说是在Template Method方法的基础上进行拆分,将表示和构建过程相分离拆成一个更加复杂但耦合度更低的代码。

2、案例分析
  • 假设需要构建一栋房子,需要墙、窗户、房顶、地板等需求,但是所需要的房子是木头还是石头还是其他材料的房子并不清楚
  • 可以很容易的使用Template Method模式写出一个伪代码,解决依赖倒置问题即可。
2.1、模板方法(一)
class House{
public:
    void Init(){
        // 构建墙
        this->BuilderWall();
        // 构建四个窗户
        for(int i = 0;i < 4;i++){
            this->BuilderWindows();
        }
        // 是否需要铺地板
        if(this->BuilderFlag()){
            this->BuilderFloor();
        }
        // 构建房顶
        this->BuilderRoof();
    }
    virtual ~House(){

    }
protected:
    virtual void BuilderWall() = 0;
    virtual void BuilderWindows() = 0;
    virtual bool BuilderFlag() = 0;
    virtual void BuilderFloor() = 0;
    virtual void BuilderRoof() = 0;
};

class StoneHouse: public House{
public:
    virtual ~StoneHouse(){

    }
protected:
    virtual void BuilderWall(){

    }
    virtual void BuilderWindows(){

    }
    virtual bool BuilderFlag(){
        return true;
    }
    virtual void BuilderFloor(){

    }
    virtual void BuilderRoof(){

    }
};

int main()
{
    House *house = new StoneHouse();
    house->Init();
}

其实这个代码写到这里已经可以了,可以不继续优化也不需要使用构建器模式。

2.2、构建器模式
  • 一个类的功能不能太复杂太过于庞大,当类的行为代码太多时可以考虑重构,将对象的表示和构建过程分离提取多个单独的类
  • 具体思路:
    • House和HouseBuilder基类,分别是一个东西的表示和构建过程
    • 对于House基类可以有多种多样的House,StoneHouse、WoodHouse、CrystalHouse…
    • 对于HouseBuilder基类为每种房子的构建提供具体的Builder构建器,其应该组合一个House基类
    • 由于构建房子的一个基本流程(算法骨架)是大致相同(相对稳定)的,因此可以将这个大致流程(算法骨架)单独提取出一个类,通过多态的性质传入不同的XXXHouseBuilder构建器进行构建不同的房子。
class House{
protected:
    Wall wall;
    Window window;
public:
    virtual ~House(){

    }
    // ...
};

class StoneHouse: public House{
public:
    virtual ~StoneHouse(){

    }
};

class HouseBuilder{
public:
    House* GetResult(){
        return house;
    }
    virtual ~HouseBuilder(){

    }
protected:
    House *house;
    virtual void BuilderWall() = 0;
    virtual void BuilderWindows() = 0;
    virtual bool BuilderFlag() = 0;
    virtual void BuilderFloor() = 0;
    virtual void BuilderRoof() = 0;
};

class StoneHouseBuilder: public HouseBuilder{
protected:
    virtual void BuilderWall(){
//        house->wall;...
    }
    virtual void BuilderWindows(){
//        house->window;...
    }
    virtual bool BuilderFlag(){

    }
    virtual void BuilderFloor(){

    }
    virtual void BuilderRoof(){

    }
};

class HouseDirector{
public:
    HouseBuilder* houseBuilder;
    HouseDirector(HouseBuilder* _houseBuilder): houseBuilder(_houseBuilder){

    }
    House *Construct(){
        // 构建墙
        houseBuilder->BuilderWall();
        // 构建四个窗户
        for(int i = 0;i < 4;i++){
            houseBuilder->BuilderWindows();
        }
        // 是否需要铺地板
        if(houseBuilder->BuilderFlag()){
            houseBuilder->BuilderFloor();
        }
        // 构建房顶
        houseBuilder->BuilderRoof();
        return houseBuilder->GetResult();
    }
};
3、总结
  • Builder模式主要用于“分步构建一个复杂的对象”。在这其中“分步骤”是一个稳定的算法,而复杂对象的各个部分则经常变化。
  • 变化点在哪里,封装哪里——Builder模式主要在于应对“复杂对象各个部分”的频繁需求变动。其缺点在于难以应对“分步骤构建算法”的需求变动。(稳定点就是缺点)
  • Builder模式中,需要注意不同语言中构造器内部调用虚函数的差别(C++ vs Java)
    • C++中无法再父类的构造方法中调用子类的虚函数(动态绑定),因为子类在此时还没有构造出来,此时如果在构造方法中调用虚函数,那么将会是静态绑定,意味着调用父类自己的虚函数。而Java等语言不是

在这里插入图片描述

相关推荐

  1. C++构造设计模式

    2024-04-11 11:34:08       35 阅读
  2. 设计模式之装饰模式 -- C++】

    2024-04-11 11:34:08       54 阅读
  3. C++ QT设计模式:解释模式

    2024-04-11 11:34:08       34 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-11 11:34:08       98 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-11 11:34:08       106 阅读
  3. 在Django里面运行非项目文件

    2024-04-11 11:34:08       87 阅读
  4. Python语言-面向对象

    2024-04-11 11:34:08       96 阅读

热门阅读

  1. Spring Boot 经典面试题(一)

    2024-04-11 11:34:08       35 阅读
  2. Python蓝桥杯赛前总结

    2024-04-11 11:34:08       29 阅读
  3. Golang 创建一个简单的内存池

    2024-04-11 11:34:08       32 阅读
  4. string的模拟实现

    2024-04-11 11:34:08       31 阅读
  5. 【Go】go mod初始化

    2024-04-11 11:34:08       38 阅读
  6. Spring-boot context.initializer.classes 配置的使用

    2024-04-11 11:34:08       34 阅读
  7. Linux如何切换root用户

    2024-04-11 11:34:08       32 阅读
  8. mongoose入门

    2024-04-11 11:34:08       33 阅读
  9. Dockfile相关

    2024-04-11 11:34:08       30 阅读