您的位置:首页 > 编程语言 > C语言/C++

C++设计模式之建造者模式

2016-03-05 03:02 671 查看

C++设计模式之建造者模式

将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

C设计模式之建造者模式
一缘由

二实现

三代码分析

四总结

一、缘由

当我们在构造一个窗口控件的时候,往往包含三个方面的初始化工作:

UI初始化

动画初始化

信号槽初始化

这样我们就可以构造好一个窗口控件了,我们可以看以下类图:



乍一看该实现并没有什么问题
CenterWidget
类在其构造函数中调用了
initUi
initAnimation
initSlot
三个私有成员函数分别进行Ui,动画,信号槽的初始化。可是当我们进行一个新的
CenterWidget
构造时,要求动画效果改变,这时候需要怎么做呢?修改
initAnimation
成员函数?作为一名优秀的程序员,提到修改类的时候就应该警惕,这个设计明显违反了开闭原则。这时候我们遇到到一个问题:构造的接口是固定的,构造的顺序是固定的,而要求构造的内容变化。

我们直觉上,这个问题的解就是:
initUi
,
initAnimation
,
initSlot
三个成员函数必须为虚函数。在需要动画、Ui或者信号槽发生变化的时候,只要需要添加新的子类,重写其中某个函数需要变化即可。可是,当这三个成员函数都成为虚函数之后,就不可能在构造函数中调用,因为

在一个类构造期间,vtable还没有初始化完成,虚函数机制不会正确工作。

于是我们增加了一个
Init
方法,在对象被构造出来之后调用之。此时类图如下:



对于这个类图来说,解决了开闭原则的问题,可是新的问题出现了,客户端需要调用
Init
,有违反了接口隔离的和单一职责原则之嫌疑,为何这么说呢?

客户端的想法是:

我想要一个
CenterWidget
实例

然后将
CenterWidget
显示出来

CenterWidget
提供的却是:

给你一个
CenterWidget
实例

然后调用
Init
接口初始化

然后将
CenterWidget
显示出来

对于客户端来说,它需要依赖一个它不需要的接口
Init
,这个
Init
不是客户端所要求的,反而更象是客户端需要
CenterWidget
作为主窗口的逻辑功能而强行搭配的一个接口(注:接口隔离原则指的是客户端不应该依赖它不需要的接口;一个类对另一个类的依赖应该建立在最小的接口上。)。其次,作为主窗口类的
CenterWidget
需要额外负责管理复杂的构造逻辑。这倒不是什么严重的问题,很多时候这种解决方法都是行之有效的,不过我们提出一种更为优雅的解决方法。

二、实现

为了解决上节所述的问题,我们将整个
CenterWidget
构造职责抽取出来由单独的一个
Builder
类承担。这样将
initUi
initAnimation
initSlot
函数转移到
Builder
类中。由
Builder
类构造出来
CenterWidget
实例,另外,有时候对
initUi
initAnimation
initSlot
三个函数调用次序有所要求,引入一个
Director
类专门管理对
initUi
initAnimation
initSlot
的调用次序,指挥
Builder
的工作,这就是建造者模式。建造者模式的示意图和使用了建造者模式的
CenterWidget
建造方案如下图所示:


建造者模式


使用建造者模式的`CenterWidget`方案

三、代码分析

下面给出
CenterWidget
方案的示例代码

[code]#include <string>
#include <iostream>

using std::string;

class CenterWidget {
private:
    string Ui;
    string Animation;
    string Slot;
public:
    virtual ~CenterWidget (){};
    void setUi(const string& x){
        Ui = x;
    }

    void setAnimation(const string& x){
        Animation = x;
    }

    void setSlot(const string& x){
        Slot = x;
    }

    void show(){
        std::cout << "Ui = " << Ui  <<
        "  Animation = " << Animation  <<
        "  Slot = " << Slot << std::endl;
    }
};

class CenterWidgetBuilder {
public:
    virtual ~CenterWidgetBuilder(){}
    virtual void initUi() = 0;
    virtual void initAnimation() = 0;
    virtual void initSlot() = 0;
    virtual CenterWidget *getResult() = 0;
};

class ConcreteCenterWidgetBuilderA : public CenterWidgetBuilder {
private:
    CenterWidget *curWidget;
public:
    ConcreteCenterWidgetBuilderA():curWidget(new CenterWidget){}
    virtual ~ConcreteCenterWidgetBuilderA(){
        delete curWidget;
    };
    virtual void initUi(){
        curWidget->setUi("Q Ui");
    }
    virtual void initAnimation(){
        curWidget->setAnimation("Biu~Biu~Biu~");
    }
    virtual void initSlot(){
        curWidget->setSlot("connected to your heart");
    }

    CenterWidget *getResult(){
        return curWidget;
    };
};

class ConcreteCenterWidgetBuilderB : public CenterWidgetBuilder {
private:
    CenterWidget *curWidget;
public:
    ConcreteCenterWidgetBuilderB():curWidget(new CenterWidget){}
    virtual ~ConcreteCenterWidgetBuilderB(){
        delete curWidget;
    };
    virtual void initUi(){
        curWidget->setUi("Q Ui");
    }
    virtual void initAnimation(){
        curWidget->setAnimation("Boom~Boom~Boom~");
    }
    virtual void initSlot(){
        curWidget->setSlot("connected to your heart");
    }

    CenterWidget *getResult(){
        return curWidget;
    };
};

class Dirctor {
private:
    CenterWidgetBuilder *Builder;
public:
    Dirctor (CenterWidgetBuilder* builder):Builder(builder){};
    virtual ~Dirctor(){delete Builder;}
    void Construct(){
        Builder->initUi();
        Builder->initAnimation();
        Builder->initSlot();
    }
};

int main(void)
{
    ConcreteCenterWidgetBuilderA *builderA = new ConcreteCenterWidgetBuilderA;
    Dirctor *directorA = new Dirctor(builderA);
    directorA->Construct();
    builderA->getResult()->show();

    ConcreteCenterWidgetBuilderB *builderB = new ConcreteCenterWidgetBuilderB;
    Dirctor *directorB = new Dirctor(builderB);
    directorB->Construct();
    builderB->getResult()->show();
}
运行结果:
Ui = Q Ui  Animation = Biu~Biu~Biu~  Slot = connected to your heart
Ui = Q Ui  Animation = Boom~Boom~Boom~  Slot = connected to your heart


ConcreteCenterWidgetBuilderB和ConcreteCenterWidgetBuilderA中有重复代码,为了方便代码复用,我们可以使用ConcreteCenterWidgetBuilderB继承ConcreteCenterWidgetBuilderA,然后重写需要变化的initAnimation即可,代码修改如下:

[code]
class ConcreteCenterWidgetBuilderA : public CenterWidgetBuilder {
protected:  //为了子类能访问之,改为protected
    CenterWidget *curWidget;
public:
    ConcreteCenterWidgetBuilderA():curWidget(new CenterWidget){}
    virtual ~ConcreteCenterWidgetBuilderA(){
        delete curWidget;
    };
    virtual void initUi(){
        curWidget->setUi("Q Ui");
    }
    virtual void initAnimation(){
        curWidget->setAnimation("Biu~Biu~Biu~");
    }
    virtual void initSlot(){
        curWidget->setSlot("connected to your heart");
    }

    CenterWidget *getResult(){
        return curWidget;
    };
};

class ConcreteCenterWidgetBuilderB : public ConcreteCenterWidgetBuilderA {
public:
    ConcreteCenterWidgetBuilderB(){}
    virtual ~ConcreteCenterWidgetBuilderB(){ };
    //只重写需要改变的部分
    virtual void initAnimation(){
        curWidget->setAnimation("Boom~Boom~Boom~");
    }
};

运行结果:
Ui = Q Ui  Animation = Biu~Biu~Biu~  Slot = connected to your heart
Ui = Q Ui  Animation = Boom~Boom~Boom~  Slot = connected to your heart


四、总结

建造者模式的主要优点 :

将产品本身和产品的创建过程解耦,使得不同的创建过程创建出不同的实例

可以很方便地增加新的建造者,实现新的产品实例的创建,符合开闭原则

建造者模式的主要缺点 :

建造者模式只能创建具有许多共同点的产品,组成成分相似

如果产品内部组成复杂多变,将需要定义大量的建造者类,使得系统复杂化

使用场景 :

需要创建的产品具有多个组成部分且内部构造复杂

需要指定产品的创建顺序

对象创建的过程需要独立于该类
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: