1. 建造者(Builder,或生成器)模式的定义
(1)将一个复杂对象构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
①上述提到的“构建”是指构造过程的算法(即构造顺序,位于director类中),“表示”指生成各部件的具体细节(或叫实现,位于Builder或其子类中)。
②由指导者(director)来指导构造过程,而建造者(builder)负责每步的对象的具体实现和组装各部件。
③指导者可以重用构建过程,而生成器是可以被切换的具体实现。
(2)建造者模式的结构和说明
①Builder:建造者接口,定义创建一个Product对象所需要的各个部件的接口。
②ConcreteBuilder:具体的建造者实现,实现各个部件的创建,并负责组装Product对象的各个部件,同时还提供一个让用户获取组装完成后的产品对象的方法。
③Director:指导员,主要用来使用Builder接口,以一个统一年过程来构建所需要的Product对象。
④Product:产品,表示被建造者构建的复杂对象,包含多个部件。
2. 建造者模式的思考
(1)建造者模式的功能:构建复杂的产品,而且是细化的、分步骤的构建产品。也就是其重在一步一步解决构造复杂对象的问题。
①构建的过程是统一的、固定不变的(则指导者来制定)。变化的是的每步的具体的实现,由建造者来实现。
②建造者模式的重心在于分离构建算法和具体构造的实现。
(2)建造者模式的构成——两个部分
①Builder接口:这里定义了如何构建各个部件,也就是知道每个部件的功能是如何实现的,以及如何装配这些部件到产品中去。即部件构造和产品装配。
②Director:知道按什么流程来构建产品,负责整体的构建算法,通常是分步骤来执行。这里要强调的是,整体构建算法是固定的。当Director实现整体构建算法的时候,遇到需要创建和组合具体部件的时候,就会把这些具体的实现委托给Builder去完成。
【编程实验】建造神舟飞船
//创建型模式:建造者模式
//神舟飞船的组装
#include <stdio.h>
#include <string>
using namespace std;
//*************************辅助类:各个部件************************
//轨道舱
class OrbitalModule
{
private:
string name;
public:
OrbitalModule(string name)
{
this->name = name;
}
void setName(string name)
{
this->name = name;
}
string getName(){return name;}
};
//发动机
class Engine
{
private:
string name;
public:
Engine(string name)
{
this->name = name;
}
void setName(string name)
{
this->name = name;
}
string getName(){return name;}
};
//逃逸塔
class EscapeTower
{
private:
string name;
public:
EscapeTower(string name)
{
this->name = name;
}
void setName(string name)
{
this->name =name;
}
string getName(){return name;}
};
//最终产品Product
class Airship
{
private:
OrbitalModule* orbitalModule; //轨道舱
Engine* engine; //发动机
EscapeTower* escapeTower; //逃逸塔
public:
OrbitalModule* getOrbitalModule(){return orbitalModule;}
void setOrbitalModule(OrbitalModule* orbitalModule)
{
this->orbitalModule =orbitalModule;
}
Engine* getEngine(){return engine;}
void setEngine(Engine* engine)
{
this->engine =engine;
}
EscapeTower* getEscapeTower(){return escapeTower;}
void setEscapeTower(EscapeTower* escapeTower)
{
this->escapeTower =escapeTower;
}
void launch()
{
//检测发动机是否正常
printf("%s\n",engine->getName().c_str());
//检测轨道舱是否正常
printf("%s\n",orbitalModule->getName().c_str());
//检测逃逸塔是否正常
printf("%s\n",escapeTower->getName().c_str());
//发射
printf("launch...\n");
}
};
//*******************************************Builder:建造者*****************************
//抽象建造者
class AirshipBuilder
{
public:
//构建发动机
virtual void buildEngine() = 0;
//构建轨道舱
virtual void buildOrbitalModule() = 0;
//构建逃逸塔
virtual void buildEscapeTower() = 0;
};
//具体建造者
class ConcreteAirshipBuilder : public AirshipBuilder
{
private:
Airship airship;
public:
//Engine部件的构建
void buildEngine()
{
Engine* engine =airship.getEngine();
if (engine != NULL)
delete engine;
engine = new Engine("Airship's Engine!");
//组装工作
airship.setEngine(engine);
}
//OrbitalModule部件的构建
void buildOrbitalModule()
{
OrbitalModule* orbitalModule =airship.getOrbitalModule();
if (orbitalModule != NULL)
delete orbitalModule;
orbitalModule = new OrbitalModule("Airship's OrbitalModule!");
//组装工作
airship.setOrbitalModule(orbitalModule);
}
//EscapeTower部件的构建
void buildEscapeTower()
{
EscapeTower* escapeTower =airship.getEscapeTower();
if (escapeTower != NULL)
delete escapeTower;
escapeTower = new EscapeTower("Airship's EscapeTower!");
//组装工作
airship.setEscapeTower(escapeTower);
}
//返回最终的整个产品(神舟飞船)
Airship& getResult(){return airship;}
~ConcreteAirshipBuilder()
{
OrbitalModule* orbitalModule = airship.getOrbitalModule();
if (orbitalModule != NULL)
delete orbitalModule;
Engine* engine =airship.getEngine();
if (engine != NULL)
delete engine;
EscapeTower* escapeTower = airship.getEscapeTower();
if (escapeTower != NULL)
delete escapeTower;
}
};
//********************************Director:指导者******************************
class Director
{
private:
AirshipBuilder* builder;
public:
Director(AirshipBuilder* builder)
{
this->builder = builder;
}
//构建过程
//1、建造者所构建的各部分是可以是先后顺序的,但本例这个顺序不明显或无关紧要。
//2、本类中只有构建的过程(流程,也就算法),组装过程放在Builder类中。
void construct()
{
//1.先构建发动机
builder->buildEngine();
//2.再构建轨道舱
builder->buildOrbitalModule();
//3.最后构建逃逸塔
builder->buildEscapeTower();
}
};
int main()
{
//客户端调用例子
//构建者
AirshipBuilder* builder = new ConcreteAirshipBuilder();
//指导者
Director* director = new Director(builder);
director->construct(); //生成最终产品
//测试
Airship& airship = ((ConcreteAirshipBuilder*)builder)->getResult();
airship.launch();
delete builder;
delete director;
return 0;
}
3. 建造者模式的实现
(1)建造者的实现
①Builder接口的实现中,每个buildPart方法中可以包含创建部件对象和组装部件的功能。
②如果实现Builder时,只创建对象,没有组装功能,这里Builder实现跟抽象工厂的实现很类似。这时的Builder接口就类似于抽象工厂的接口。Builder的具体实现就类似于具体的工厂。从这点上看,Builder与抽象工厂很类似。
(2)指导者的实现
①指导者承担的是整体构建算法的部分,是相对不变的部分。因此在实现指导者的时候,把变化的部分分离出去是很重要的。
②指导员分离出去的变化部分,就到了建造者那里,指导者知道整体的构建算法,却不知道如何具体的创建和装配部件对象。
③在指导者的实现可以有较为复杂的算法和运算过程,在运算过程中根据需要,才会调用建造者的方法来生成部件对象。
(3)指导者和建造者的交互
①指导者和建造者一般是通过Builder的buildPart方法来进行交互的。
②实际开发中,指导者可以按整体构建算法的步骤进行运算。到了某一步骤,需要具体创建某个部件对象了,再调用Builder中创建相应部件的方法来创建具体部件。同时把前面运算的数据传递给Builder,因为Builder内部实现创建和组装部件的时候,可能会用到这些数据。
③Builder创建完具体的部件对象后,会把创建好的部件对象返回指导者。指导者继续后续的算法运算,如此反复下去,直到最终产品对象创建完毕。
(4)返回装配好的产品的方法
①标准的生成器模式中,在Builder实现里面会提供一个返回装配好的产品的方法,在Builder接口上是没有的。它考虑的是最终的对象一定要通过部件构建和装配,才算真正创建了。
②虽然指导者也参与其中,但它不负再具体部件的创建和组装,因此客户端是从Builder的实现里面获取最终装配好的产品。但也可以把这个方法加到Builder接口中。
4. 建造者模式的优点
(1)松散耦合
用同一个构建算法构建出表现上完全不同的产品,实现产品构建和产品表现上的分离。Builder模式中构建算法可以复用,而具体产品表现可以灵活、方便地扩展和切换。
(2)很容易改变产品的内部表示
建造者只提供接口给Director使用,具体的部件创建和装配方式被Builder接口隐藏了,Director并不知道实现的细节。这样要改变产品的内部表示,只需要切换Builder的具体实现即可。不用管Director。
(3)更好的复用:Director中构建产品过程的算法相对固定,可以复用。
5. Builder模式的使用场合
(1)相同的方法,不同的执行顺序,产生不同的事件结构时,可以采用建造者模式。
(2)多个部件或零件,都可以装配到一个对象中,但是产生的运行结果又不相同时,则可以使用该模式。
(3)产品类非常复杂,或者产品类中的调用顺序不同产生了不同的效能,这个可以使用该模式。
6. 其他
(1)Builder与FactoryMethod模式结合
在Builder的实现中,通常需要选择具体的部件实现,这时可以用工厂方法来实现,通过工厂方法来获取具体的部件对象,然后再进行部件的装配。
(2)Builder与抽象工厂模式的区别
①Builder模式着重于一步步构造一个复杂对象,而Abstract Factory着重于多个系列的产品对象(简单的或复杂的)
②Builder是最后一步返回产品,而Abstract Factory是立即返回产品
③抽象工厂侧重点是创建,创建零件是它的主要职责,他关心的是创建产品的结果,而builder侧重的是创建产品的过程,即顺序安排。
(3)Builder与模板方法的区别
①两者都是定义了方法执行的流程。但一个前者是在Director定义的,后者是在父类定义的。
②在实现方法的结构上,建造者模式中使用的是组合的方式,而模板方法模式采用的是继承的方式
③模板方法模式是通过把不变行为搬移到父类(超类),去除了类中的重复代码来体现它的优势。而建造者是将不变部分移到Director中去,变化的部分由Builder接口去实现。
【编程实验】将数据导出为txt和xml格式的文件
①文件头:包括部门ID、导出数据的日期
②文件体:表名称、分条描述数据
③文件尾:输出人的信息
//创建型模式:建造者模式
//将数据导出为不同格式的文件
#include <iostream>
#include <string>
#include <sstream>
#include <list>
using namespace std;
//*************************辅助类:各个部件************************
//文件头对象
class Header
{
private:
string depId; //部门编号
string date; //导出数据的日期
public:
void setDepId(string depId)
{
this->depId = depId;
}
string& getDepId(){return depId;}
void setDate(string date)
{
this->date = date;
}
string& getDate(){return date;}
};
//文件体
class Body
{
private:
string productId; //产品编号
double price; //销售价格
double amount; //销售数量
public:
void setProductId(string productId)
{
this->productId = productId;
}
string& getProductId(){return productId;}
void setPrice(double price)
{
this->price = price;
}
double getPrice(){return price;}
void setAmount(double amount)
{
this->amount = amount;
}
double getAmount(){return amount;}
};
//文件尾
class Footer
{
private:
string user; //输出人
public:
void setUser(string user)
{
this->user = user;
}
string& getUser(){return user;}
};
//*******************************Builder*************************
class Builder
{
public:
virtual void buildHeader(Header& header) = 0;
virtual void buildBody(list<Body>& bodys) = 0;
virtual void buildFooter(Footer& footer) = 0;
};
//XmlBuilder
class XmlBuilder : public Builder
{
private:
string buffer; //用来记录文件的内容,相当于最终产品
public:
void buildBody(list<Body>& bodys)
{
buffer.append(" <Body>\n");
buffer.append(" <Datas TableName=Sale Details:>\n");
list<Body>::iterator first = bodys.begin();
list<Body>::iterator last = bodys.end();
ostringstream oss;
while (first != last)
{
oss.str("");
oss<<first->getPrice();
buffer.append(" <Data>\n");
buffer.append(" <ProductId>"+first->getProductId()+"</ProductId>\n");
buffer.append(" <Price>"+oss.str()+"</Price>\n");
oss.str("");
oss<<first->getAmount();
buffer.append(" <Amount>"+oss.str()+"</Amount>\n");
buffer.append(" </Data>\n");
++first;
}
buffer.append(" </Datas>\n");
buffer.append(" </Body>\n");
};
void buildFooter(Footer& footer)
{
buffer.append(" <Footer>\n");
buffer.append(" <ExportUser>"+footer.getUser()+"\n");
buffer.append(" </Footer>\n");
buffer.append("</Report>\n");
}
void buildHeader(Header& header)
{
buffer.append("<?xml version ='1.0' encoding ='gb2312'?>\n");
buffer.append("<Report>\n");
buffer.append(" <Header>\n");
buffer.append(" <DepId>"+header.getDepId()+"</DepId>\n");
buffer.append(" <ExportDate>"+header.getDate()+"</ExportDate>\n");
buffer.append(" </Header>\n");
}
string& getResult(){return buffer;}
};
//TxtBuilder
class TxtBuilder : public Builder
{
private:
string buffer; //用来记录文件的内容,相当于最终产品
public:
void buildBody(list<Body>& bodys)
{
list<Body>::iterator first = bodys.begin();
list<Body>::iterator last = bodys.end();
buffer.append("Sale details:\n");
ostringstream oss;
while (first != last)
{
buffer.append("ProductId="+first->getProductId()+", ");
oss.str("");
oss << first->getPrice();
buffer.append("Price=" + oss.str()+", ");
oss.str("");
oss << first->getAmount();
buffer.append("Amount=" + oss.str()+"\n");
++first;
}
}
void buildFooter(Footer& footer)
{
buffer.append(footer.getUser());
}
void buildHeader(Header& header)
{
buffer.append(header.getDepId()+","+header.getDate() + '\n');
}
string& getResult(){return buffer;}
};
//*********************************Director*********************
class Director
{
private:
Builder* builder;
public:
Director(Builder* builder){this->builder = builder;}
void construct(Header& header,list<Body>& bodys,Footer& footer)
{
builder->buildHeader(header);
builder->buildBody(bodys);
builder->buildFooter(footer);
}
};
int main()
{
//客户端调用例子
//准备测试数据
Header* header = new Header();
header->setDepId("one company");
header->setDate("2016-5-18");
list<Body> bodys;
Body* body1 = new Body();
body1->setProductId("001");
body1->setPrice(100);
body1->setAmount(80);
Body* body2 = new Body();
body2->setProductId("002");
body2->setPrice(99);
body2->setAmount(55);
bodys.push_back(*body1);
bodys.push_back(*body2);
Footer* footer = new Footer();
footer->setUser("SantaClaus");
//测试输出到文本文件
TxtBuilder* txtBuilder = new TxtBuilder();
//创建指导者对象
Director* directorTxt = new Director(txtBuilder);
directorTxt->construct(*header,bodys,*footer);
cout << txtBuilder->getResult() << endl;
//测试输出到XML文件
XmlBuilder* xmlBuilder = new XmlBuilder();
//创建指导者对象
Director* directorXml = new Director(xmlBuilder);
directorXml->construct(*header,bodys,*footer);
cout << xmlBuilder->getResult() << endl;
delete txtBuilder;
delete directorTxt;
delete xmlBuilder;
delete directorXml;
delete header;
delete body1;
delete body2;
delete footer;
return 0;
}