解决的问题:
我创建的这个对象比较复杂,且该对象里面的成员函数用不同的实现来表示不同的实例,换句话说就是同样的对象构建过程可以有不同的表示。比如我那天去吃过桥米线,他们有不同的套餐,套餐里包含的种类是一样的,都有一碗米线,一份凉菜,一杯饮料。但是不同的套餐里这3样又都不是全部一样的。此时我们就可以用建造者模式。
类图结构:
1.建造者(Builder)角色:给出一个抽象接口,以规范产品对象的各个组成成分的建造。一般而言,此接口独立于应用程序的商业逻辑。模式中直接创建产品对象的是具体建造者(Concrete Builder)角色。具体建造者类必须实现这个接口所要求的方法:一个是建造方法,另一个是结果返还方法。此时就是米线店的员工,按照收银员的要求的去准备具体的套餐,放入适当的米线,凉菜和饮料。
2.具体建造者(Concrete Builder)角色:担任这个角色的是于应用程序紧密相关的类,它们在应用程序调用下创建产品实例。这个角色主要完成的任务包括:实现Builder角色提供的接口,一步一步完成创建产品实例的过程。在建造过程完成后,提供产品的实例。是具体的做某个套餐的员工。
3.指导者(Director)角色:担任这个角色的类调用具体建造者角色以创建产品对象。导演者并没有产品类的具体知识,真正拥有产品类的具体知识的是具体建造者对象。是收银员,他知道我想要什么套餐,他会告诉里面的米线店员工去准备什么套餐。
4.产品(Product)角色:产品便是建造中的复杂对象。指导者角色是于客户端打交道的角色。导演者角色将客户端创建产品的请求划分为对各个零件的建造请求,再将这些请求委派给具体建造者角色。具体建造者角色是做具体建造工作的,但却不为客户端所知。就是最后的套餐,所有东西放到一起端过来。
样例实现:
// CplusplusBuild.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <vector> #include <string> #include <iostream> using namespace std; //product class Food { private:vector<string> mFoodName; vector<int> mFoodPrice; public: void add(string foodName,int price) { mFoodName.push_back(foodName); mFoodPrice.push_back(price); } void show() { cout<<"Food List" <<endl; cout<<"-------------------"<<endl; for(int i=0;i<mFoodName.size();i++) { cout<<mFoodName[i]<<" "<<mFoodPrice[i]<<endl; } } }; //builder class Builder { public: virtual void BuildRiceNoodles() {}; virtual void BuildCoolDish(){}; virtual void BuildDrink(){}; virtual Food * getFood(){return NULL;} }; //builderA class BuilderA:public Builder { private: Food *food; public: BuilderA(){food = new Food();} void BuildRiceNoodles() { food->add("RiceNoodlesA",20); } void BuildCoolDish() { food->add("CoolDishA",20); } void BuildDrink() { food->add("DrinkA",20); } Food * getFood() { return food; } }; //builderB class BuilderB:public Builder { private: Food *food; public: BuilderB(){food = new Food();} void BuildRiceNoodles() { food->add("RiceNoodlesB",10); } void BuildCoolDish() { food->add("CoolDishB",10); } void BuildDrink() { food->add("DrinkB",10); } Food * getFood() { return food; } }; //director class FoodManager { public: void Construct(Builder * builder) { builder->BuildRiceNoodles(); builder->BuildDrink(); builder->BuildCoolDish(); } }; //clent int _tmain(int argc, _TCHAR* argv[]) { FoodManager *foodManager= new FoodManager(); Builder * builder = new Builder(); // the following code can use simple factory; char ch; cout<<"input your food Type (A or B):"; cin>>ch; if(ch=='A') { builder = new BuilderA(); }else if(ch=='B') { builder = new BuilderB(); } foodManager->Construct(builder); Food * food = builder->getFood(); food->show(); return 0; }
建造者模式的扩展:
建造者模式在使用过程中可以演化出多种形式:
省略抽象建造者角色
如果系统中只需要一个具体的建造者的话,可以省略掉抽象建造者。这是代码可能如下:
//director class FoodManager { private: BuilderA * builder; public: FoodManager() {builder = new BuilderA();}; void Construct() { builder->BuildRiceNoodles(); builder->BuildDrink(); builder->BuildCoolDish(); } };
省略指导者角色
在具体建造者只有一个的情况下,如果抽象建造者角色已经被省略掉,那么还可以省略掉指导者角色,让Builder自己扮演指导者和建造者双重角色。这是代码可能如下:
//builder class Builder { private: Food * food; public: Builder(){food = new Food();} void BuildRiceNoodles() {//..}; void BuildCoolDish(){//..}; void BuildDrink(){//..}; Food * getFood(){return food;} void Construct() { BuildRiceNoodles(); BuildCoolDish(); BuildDrink(); } };
同时,客户端也需要进行相应的调整,如下:
//client int _tmain(int argc, _TCHAR* argv[]) { Builder * builder = new Builder(); builder->Construct(); Food *food = builder->getFood(); food->show(); return 0; }
C#中的StringBuilder就是这样一个例子。
实现要点:
1.建造者模式主要用于“分步骤构建一个复杂的对象”,在这其中“每个步骤”是一个稳定的算法,而复杂对象的各个步骤之间则经常变化。
2.上一篇所说的抽象工厂模式解决“系列对象”的需求变化,而建造者模式解决单个对象里“对象部分”的需求变化。
产品不需要抽象类,特别是由于创建对象的算法复杂而导致使用此模式的情况下或者此模式应用于产品的生成过程,其最终结果可能差异很大,不大可能提炼出一个抽象产品类。
3.创建者中的创建子部件的接口方法不是抽象方法而是空方法,不进行任何操作,具体的创建者只需要覆盖需要的方法就可以,但是这也不是绝对的,特别是类似文本转换这种情况下,缺省的方法将输入原封不动的输出是合理的缺省操作。
适用性:
以下情况应当使用建造者模式:
1、需要生成的产品对象有复杂的内部结构。
2、需要生成的产品对象的属性相互依赖,建造者模式可以强迫生成顺序。
3、 在对象创建过程中会使用到系统中的一些其它对象,这些对象在产品对象的创建过程中不易得到。
效果
1、建造者模式的使用使得产品的内部表象可以独立的变化。使用建造者模式可以使客户端不必知道产品内部组成的细节。
2、每一个Builder都相对独立,而与其它的Builder无关。
3、可使对构造过程更加精细控制。
4、将构建代码和实现代码分开。
5、建造者模式的缺点在于难于应付“分步骤构建算法”的需求变动。
LCL_data原创于CSDN.NET【http://blog.csdn.net/lcl_data/article/details/8758477】