本文共 4630 字,大约阅读时间需要 15 分钟。
目录
builder模式将对象的创建与对象本身解耦。该模式的主要思想是,对象不必为其自己的创建负责。在实际项目中,有的实例对象可能非常复杂,拥有很多成员,初始化这种对象,本身就是一项复杂繁琐的任务,因此可以将该任务委托给另一个类来构建。
下面例子是描述一辆汽车构造的类,拥有多个成员。
#includeusing 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;}
这些众多的成员参数中,有些比较重要,有些可以忽略。如果我们只想赋值最重要的几个成员时,该怎么办呢?
一种可行的方案是使用叠进式的构造方法来实现。第1个构造方法,全部使用默认参数,第2个构造方法,增加一个指定的参数,第3个构造方法,继续增加一个指定参数,依次类推。
#include#include 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的默认值。这个可能不是调用者希望得到的。另一种容易想到的方案是,类似于java bean的常用模式,使用一个默认的构造函数,再给每一个成员,分配相应的set,get方法。
#include#include 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。根据GO4,builder模式目的是将复杂对象的构造与其表示分离,以便相同的构造过程可以创建不同的表示形式。
具体实现上来说,就是使用公共的静态内部类,来进行多成员的对象的构造。#include#include 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;}
可以看到,此方案可以链式的调用,进行对象构造和参数赋值。代码可读性非常好,暴露给调用者的接口也非常清晰。
转载地址:http://dleji.baihongyu.com/