目录
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;
}
可以看到,此方案可以链式的调用,进行对象构造和参数赋值。代码可读性非常好,暴露给调用者的接口也非常清晰。