设计模式(3) - Builder建造者模式

目录

1.模式简介

2.模式详解

2.1叠进式构造

2.2JavaBean模式

3.模式实现


1.模式简介

builder模式将对象的创建与对象本身解耦。该模式的主要思想是,对象不必为其自己的创建负责。在实际项目中,有的实例对象可能非常复杂,拥有很多成员,初始化这种对象,本身就是一项复杂繁琐的任务,因此可以将该任务委托给另一个类来构建。

2.模式详解

下面例子是描述一辆汽车构造的类,拥有多个成员。

#include <string>

using namespace std;

//描述一辆汽车的构造部件
class Vehicle 
{
private:
    string brand;   //品牌
    string engine;  //发动机(几缸)
    string tire;    //轮胎尺寸(18/19/20)
    string seat;    //座位类型(织物/真皮)
    string fuel;    //燃油(92,95,98)

public:
    Vehicle(string brand, string engine, string tire, string seat, string fuel):
        brand(brand), engine(engine),tire(tire),seat(seat),fuel(fuel)
    {
    }
};

int main()
{
    Vehicle vh("VOLVO", "V6", "20", "leather", "95");
    return 0;
}

这些众多的成员参数中,有些比较重要,有些可以忽略。如果我们只想赋值最重要的几个成员时,该怎么办呢?

2.1叠进式构造

一种可行的方案是使用叠进式的构造方法来实现。第1个构造方法,全部使用默认参数,第2个构造方法,增加一个指定的参数,第3个构造方法,继续增加一个指定参数,依次类推。

#include <string>
#include <iostream>

using namespace std;

//描述一辆汽车的构造部件
class Vehicle
{
private:
    string brand;   //品牌
    string engine;  //发动机(几缸)
    string tire;    //轮胎尺寸(18/19/20)
    string seat;    //座位类型(织物/真皮)
    string fuel;    //燃油(92,95,98)

public:
    //--------叠进式构造--------
    Vehicle() :
        Vehicle("") {
        cout << "level1" << endl;
    }
    Vehicle(string brand) :
        Vehicle(brand, "") {
        cout << "level2" << endl;
    }
    Vehicle(string brand, string engine) :
        Vehicle(brand, engine, "") {
        cout << "level3" << endl;
    }
    Vehicle(string brand, string engine, string tire) :
        Vehicle(brand, engine, tire, "") {
        cout << "level4" << endl;
    }
    Vehicle(string brand, string engine, string tire, string seat) :
        Vehicle(brand, engine, tire, seat, "") {
        cout << "level5" << endl;
    }
    Vehicle(string brand, string engine, string tire, string seat, string fuel) :
        brand(brand), engine(engine), tire(tire), seat(seat), fuel(fuel)
    {
        cout << "level6" << endl;
    }
};

int main()
{
    Vehicle vh("VOLVO");
    return 0;
}

运行结果:
level6
level5
level4
level3
level2

此方案确实可以实现上面功能,只指定某些参数生效。但是缺点也很明显:
--代码扩展性和可读性差。如果参数特别多,需要相应增加很多构造方法。代码难以阅读和理解。
--代码太过于耦合。如果调用者只想设置brand和seat,那还强行的设置了engine和tire的默认值。这个可能不是调用者希望得到的。

2.2JavaBean模式

另一种容易想到的方案是,类似于java bean的常用模式,使用一个默认的构造函数,再给每一个成员,分配相应的set,get方法。
 

#include <string>
#include <iostream>

using namespace std;

//描述一辆汽车的构造部件
class Vehicle
{
private:
    string brand;   //品牌
    string engine;  //发动机(几缸)
    string tire;    //轮胎尺寸(18/19/20)
    string seat;    //座位类型(织物/真皮)
    string fuel;    //燃油(92,95,98)

public:
    Vehicle() {}
    string getBrand() { return brand; }
    void setBrand(string brand) { this->brand = brand; }
    string getEngine() { return engine; }
    void setEngine(string engine) { this->engine = engine; }
    string getTire() { return tire; }
    void setTire(string tire) { this->tire = tire; }
};

int main()
{
    Vehicle vh;
    cout << "brand is:" << vh.getBrand() << endl;
    vh.setBrand("VOLVO");
    cout << "brand is:" << vh.getBrand() << endl;
    return 0;
}

运行结果:
brand is:
brand is:VOLVO

此方案比第一种方案,变得更灵活了。可以指定任何的参数,并且对其它参数没有影响。但是也有一些明显的缺点:
--代码逻辑耦合。每次构建了一个默认的对象后,必须调用众多个set函数才算是真正构造完成。如果在set调用完成之前,有其它逻辑来使用了此对象,会出现逻辑问题。
--扩展性较差。每次新增成员属性,必须相应的增加get/set。

3.模式实现

根据GO4,builder模式目的是将复杂对象的构造与其表示分离,以便相同的构造过程可以创建不同的表示形式。
具体实现上来说,就是使用公共的静态内部类,来进行多成员的对象的构造。

#include <string>
#include <iostream>

using namespace std;

//描述一辆汽车的构造部件
class Vehicle
{
private:
    string brand;   //品牌
    string engine;  //发动机(几缸)
    string tire;    //轮胎尺寸(18/19/20)
    string seat;    //座位类型(织物/真皮)
    string fuel;    //燃油(92,95,98)

public:
    class Builder {
    public:
        string innerBrand = "";
        string innerEngine = "";
        string innerTire = "";
        string innerSeat = "";
        string innerFuel = "";
    public:
        Vehicle build() {
            return Vehicle(*this);
        }
        Builder* brand(string brand) {
            this->innerBrand = brand;
            return this;
        }
        Builder* engine(string engine) {
            this->innerEngine = engine;
            return this;
        }
        Builder* tire(string tire) {
            this->innerTire = tire;
            return this;
        }
        Builder* seat(string seat) {
            this->innerSeat = seat;
            return this;
        }
        Builder* fuel(string fuel) {
            this->innerFuel = fuel;
            return this;
        }
    };

    Vehicle(const Builder &bd) : 
        brand(bd.innerBrand), engine(bd.innerEngine), tire(bd.innerTire),
        seat(bd.innerSeat), fuel(bd.innerFuel) {}
};

int main()
{
    Vehicle vh = Vehicle::Builder().brand("VOLVO") \
        ->engine("V6") \
        ->tire("20") \
        ->seat("leather") \
        ->fuel("95") \
        ->build();
    return 0;
}

可以看到,此方案可以链式的调用,进行对象构造和参数赋值。代码可读性非常好,暴露给调用者的接口也非常清晰。

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值