设计模式学习–建造者模式
文章理论部分参考以下链接
http://www.runoob.com/design-pattern/abstract-factory-pattern.html
一、建造者模式介绍
建造者模式(Builder Pattern)使用多个简单的对象一步一步构建成一个复杂的对象。一个 Builder 类会一步一步构造最终的对象,该 Builder 类是独立于其他对象的。这种类型的设计模式属于创建型模式。
意图:将一个复杂的构建与其表示相分离,使得同样的构建过程可以创建不同的表示。
主要解决:主要解决在软件系统中,有时候面临着”一个复杂对象”的创建工作,其通常由各个部分的子对象用一定的算法构成;由于需求的变化,这个复杂对象的各个部分经常面临着剧烈的变化,但是将它们组合在一起的算法却相对稳定。
何时使用:一些基本部件不会变,而其组合经常变化的时候。
如何解决:将变与不变分离开。
关键代码:建造者:创建和提供实例,导演:管理建造出来的实例的依赖关系。
应用实例: 1、去肯德基,汉堡、可乐、薯条、炸鸡翅等是不变的,而其组合是经常变化的,生成出所谓的”套餐”。 2、JAVA 中的 StringBuilder。
优点: 1、建造者独立,易扩展。 2、便于控制细节风险。
缺点: 1、产品必须有共同点,范围有限制。 2、如内部变化复杂,会有很多的建造类。
使用场景: 1、需要生成的对象具有复杂的内部结构。 2、需要生成的对象内部属性本身相互依赖。3、当构造过程必须允许被构造的对象有不同的表示时。
注意事项:与工厂模式的区别是:建造者模式更加关注与零件装配的顺序。
**抽象基类:**Builder:这个基类是全部创建对象过程的抽象,提供构建不同组成部分的接口函数
接口:
1、Builder::BuildPart函数:是对一个对象不同部分的构建函数接口,由Builder的派生类来具体实现.
2、Director::Construct函数:通过调用上面的各个接口函数完成对象的构建。
也就是说各个不同部分装配的过程都是一致的(同样的调用的Construct函数),但是不同的构建方式会有不同的表示(根据Builder的实际类型来决定如何构建,也就是多态)
解析:
建造者模式是基于这样的一个情况:比如一辆单车,都是由车轮车座等等的构成的(一个对象不同的组成部分),不同的品牌生产出来的也不一样(不同的构建方式)。虽然不同的品牌构建出来的单车不同,但是构建的过程还是一样的。也就是说Construct函数中固定了各个组成部分的装配方式,而具体是装配怎样的组成部分由Builder的派生类实现。
二、实现演示
1、整体的代码,先出现UML结构图中对应的代码,熟悉该模式的过程。
#include<iostream>
using namespace std;
//虚拟基类,是所有Builder的基类,提供不同部分的构建接口函数
class Builder
{
public:
Builder(){}
virtual ~Builder(){}
//纯虚函数,提供构建不同部分的构建接口函数
virtual void BuilderPartA()=0;
virtual void BuilderPartB()=0;
};
//使用Builder构建产品,构建产品的过程都一致,但是不同的builder有不同的实现这个不同的实现通过不同的Builder派生类来实现,存有一个Builder的指针,通过这个来实现多态调用
class Director
{
public:
Director(Builder* pBuilder):m_pBuilder(pBuilder){}
~Director()
{
delete m_pBuilder;
m_pBuilder = NULL;
}
// Construct函数表示一个对象的整个构建过程,不同的部分之间的装配方式都是一致的,首先构建PartA其次是PartB,只是根据不同的构建者会有不同的表示
void Construct()
{
m_pBuilder->BuilderPartA();
m_pBuilder->BuilderPartB();
}
private:
Builder* m_pBuilder;
};
//Builder的派生类,实现BuilderPartA和BuilderPartB接口函数
class ConcreteBuilder1:public Builder
{
public:
ConcreteBuilder1(){}
virtual ~ConcreteBuilder1(){}
virtual void BuilderPartA()
{
cout<<"BuilderPartA by ConcreteBuilder1"<<endl;
}
virtual void BuilderPartB()
{
cout<<"BuilderPartB by ConcreteBuilder1"<<endl;
}
};
//Builder的派生类,实现BuilderPartA和BuilderPartB接口函数
class ConcreteBuilder2:public Builder
{
public:
ConcreteBuilder2(){}
virtual ~ConcreteBuilder2(){}
virtual void BuilderPartA()
{
cout<<"BuilderPartA by ConcreteBuilder2"<<endl;
}
virtual void BuilderPartB()
{
cout<<"BuilderPartB by ConcreteBuilder2"<<endl;
}
};
int main()
{
Builder* pBuilder1 = new ConcreteBuilder1;
Director* pDirector1 = new Director(pBuilder1);
pDirector1->Construct();
Builder* pBuilder2 = new ConcreteBuilder2;
Director* pDirector2 = new Director(pBuilder2);
pDirector2->Construct();
delete pDirector1;
delete pDirector2;
return 0;
}
2、仿写一个的例子,这里将Runoob中的例子简化加改写。
假设一个快餐店的商业案例,其中,一个典型的套餐是一个汉堡、一杯饮料和一份薯条。其中汉堡可以是牛肉汉堡或鳕鱼汉堡等。而饮料可以是可口可乐,也可以奶茶。虽然种类很多,但最后组成的套路包含三个基本元素不变。
(1)首先创建了一个套餐的基类
//类似Builder基类,提供不同部分的构建接口函数
class Meal
{
public:
Meal(){}
virtual ~Meal(){}
//纯虚函数,提供构建不同部分的构建接口函数
virtual void Burger()=0;
virtual void Drink()=0;
virtual void Fries()=0;
};
(2)再创建了一个配餐员的类,它来把汉堡,饮料和薯条配齐。
class Director
{
public:
Director(Meal* pMeal):m_pMeal(pMeal){}
~Director()
{
delete m_pMeal;
m_pMeal = NULL;
}
// Construct函数表示一个对象的整个构建过程,不同的部分之间的装配方式都是一致的
void Construct()
{
m_pMeal->Burger();
m_pMeal->Drink();
m_pMeal->Fries();
}
private:
Meal* m_pMeal;
};
(3)加入两款具体的套餐
//牛肉堡套餐
class BeefMeal:public Meal
{
public:
BeefMeal(){}
virtual ~BeefMeal(){}
virtual void Burger()
{
cout<<"Add a Beef Burger"<<endl;
}
virtual void Drink()
{
cout<<"Cola"<<endl;
}
virtual void Fries()
{
cout<<"Fries"<<endl;
}
};
//鳕鱼堡套餐
class FishMeal:public Meal
{
public:
FishMeal(){}
virtual ~FishMeal(){}
virtual void Burger()
{
cout<<"Add a Fish Burger"<<endl;
}
virtual void Drink() //奶茶配鱼肉
{
cout<<"Milk Tea"<<endl;
}
virtual void Fries()
{
cout<<"Fries"<<endl;
}
};
(4)
int main()
{
Director* pDirector1 = new Director(new BeefMeal);
pDirector1->Construct();
Director* pDirector2 = new Director(new FishMeal);
pDirector2->Construct();
delete pDirector1;
delete pDirector2;
return 0;
}
三、拓展性
如果想再新添,什么鸡肉堡套餐,只需要新加一个鸡肉堡套路的类(当然鸡肉堡这个实现也会新加,但这不考虑)。但是如果套餐的组成变了,比如说套餐中新加了鸡翅等,那需要修改的地方还是挺多的。