博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
设计模式(3) - Builder建造者模式
阅读量:4071 次
发布时间:2019-05-25

本文共 4630 字,大约阅读时间需要 15 分钟。

目录


1.模式简介

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

2.模式详解

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

#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 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 
#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的默认值。这个可能不是调用者希望得到的。

2.2JavaBean模式

另一种容易想到的方案是,类似于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。

3.模式实现

根据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/

你可能感兴趣的文章
postgresql查看表的和索引的情况,判断是否膨胀
查看>>
postgresql中根据oid和filenode去找表的物理文件的位置
查看>>
postgresql中wal日志什么时候会触发归档
查看>>
Centos 6.8 上 DRBD安装和使用
查看>>
history查看历史操作记录,并显示操作时间
查看>>
postgresql修改完端口后直接psql连接数据库报错
查看>>
pl/proxy-2.5安装在postgresql9.6上无法编译
查看>>
postgresql遇到“Connection refused”和“No route to host”大概的解决方法
查看>>
postgresql上安装sysbench-0.5
查看>>
pgpool3.2.9中编译 pgpool-walrecrunning函数出错
查看>>
benchmarksql在postgresql上的安装、使用
查看>>
使用yum安装postgresql 10 dev
查看>>
postgresql减少wal日志生成量的方法
查看>>
postgresql使用RHCS套件搭建HA高可用集群
查看>>
postgresql initdb过程中大体做了什么
查看>>
linux下的mysql源码安装
查看>>
plsql连接oracle出现ORA-12154: TNS: 无法解析指定的连接标识符
查看>>
oracle 查看库中每个表所占的空间大小
查看>>
流复制中的问题max_connection
查看>>
在mysql中使用模糊查询时,使用中文查询结果不正确问题
查看>>