设计模式——生成器(建造者)模式

1. 简介

  建造者(Builder)模式又称 生成器模式,属于创建型模式。在软件的设计中,我们可能经常会遇到需要构建某个复杂的对象(比如在游戏开发中,进行人物角色的构建),建造该对象的“过程”是稳定的(对于一个人设来都有身体,脸,发型,手脚等),而具体建造的“细节”是不同的(每个人设的身体,脸等各有千秋)。但对于用户来讲,我才不管这些,我只想告诉你,我现在需要某个对象(拥有某特征的人物角色),于是你就创建一个给我就行了。
  如果你需要将一个复杂对象的 构建与它的表示分离,使得同样的构建过程可以创建不同的表示的意图时,我们需要应用于一个设计模式,“建造者(Builder)模式”,又叫生成器模式。建造者模式可以将一个产品的内部表象与产品的生产过程分割开来,从而可以使一个建造过程生成具有不同的内部表象的产品对象。如果我们使用了建造者模式,那么用户就只需指定需要建造的类型就可以得到它们,而具体建造的过程和细节就不需要知道了。

2. 结构

  此模式的结构图如下所示:
设计模式——生成器(建造者)模式_第1张图片
  • Builder:是为创建一个Product对象的各个部件指定的抽象接口。
  • ConcreteBuilder:实现Builder的接口以构造和装配该产品的各个部件。
  • Director:构建一个使用Builder接口的对象。
  • Product:表示被构建的复杂对象。
  客户创建Director对象,并用它所想要的Builder对象进行配置;一旦产品部件被生成,导向器就会通知生成器;生成器处理导向器的请求,并将部件添加到该产品中;客户从生成器中检索产品。

3. 实例分析

  考虑这么一个应用场景:例如我们正在开发某个游戏程序,其中就需要进行人物的构建。整个游戏中的人设可谓五花八门,不同的游戏场景下,需要的人设有所不同,但是这些人设的组成却是相似的过程,每个人设都需要有头部、身体、手脚等。
  我们知道一个游戏的人设就是一个复杂的对象,在构建出一个人设时我们需要相应的人设的头部、发型、身体、手脚、服饰等来组成我们需要的人设。我们对人设时如何创建的过程和细节不感兴趣,我们所 需要的是指定某种类型的人设就可以得到它们。
  我们仔细分析会发现,人设的建造“过程”是稳定的,都是由头部、发型、身体、手脚、服饰等来组成的,而具体建造的“细节”是不同的,每个人设都有属于自己的头部、发型、身体、手脚、服饰等。对此,自然联系到我们谈到的设计模式——建造者模式。
/*********************************************************************************
*Copyright(C),Your Company
*FileName:  Product.h
*Author:  Huangjh
*Version:
*Date:  2017-10-29
*Description:  建造者具体类
*Others:
**********************************************************************************/
#ifndef _PRODUCT_H
#define _PRODUCT_H

#include 
#include 
#include 

class CProudct
{
public:
	CProudct() { }
	~CProudct() { }

	void add(std::string strPart)
	{
		m_listParts.push_back(strPart);
	}

	void show(void)
	{
		std::cout << "产品创建开始---------" << std::endl;
		for (std::list::iterator iter = m_listParts.begin();
			iter != m_listParts.end(); ++iter)
		{
			std::cout << *iter << std::endl;
		}
		std::cout << "产品创建完成---------" << std::endl;
	}

private:
	std::list m_listParts;
};

#endif	//#ifndef _PRODUCT_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName:  Builder.h
*Author:  Huangjh
*Version:
*Date:  2017-10-29
*Description:  为创建一个Product对象的各个部件指定的抽象接口
*Others:
**********************************************************************************/
#ifndef _BUILDER_H
#define _BUILDER_H

#include "Product.h"

class CBuilder
{
public:
	CBuilder() { }
	virtual ~CBuilder() { }

	virtual void BuilderHead(void) = 0;
	virtual void BuilderBody(void) = 0;
	virtual void BuilderHand(void) = 0;
	virtual void BuilderFoot(void) = 0;

	virtual CProudct *GetResult(void)
	{
		return NULL;
	}
};

#endif	//#ifndef _BUILDER_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName:  ConcreteBuilderThin.h
*Author:  Huangjh
*Version:
*Date:  2017-10-29
*Description:  具体建造者——比较瘦的人设
*Others:
**********************************************************************************/
#ifndef _CONCRETE_BUILDER_THIN_H
#define _CONCRETE_BUILDER_THIN_H

#include 
#include "Builder.h"
#include "Product.h"

class CConcreteBuilderThin	:	public CBuilder
{
public:
	CConcreteBuilderThin()
	{ 
		try
		{
			m_pProduct = new CProudct();
		}
		catch (std::bad_alloc &ex)
		{
			std::cout << "new CProudct:" << ex.what() << std::endl;
			abort();
		}
	}

	~CConcreteBuilderThin()
	{
		delete m_pProduct;
	}

	void BuilderHead(void)
	{
		m_pProduct->add("一个瘦的头部形象");
	}

	void BuilderBody(void)
	{
		m_pProduct->add("一个瘦的身体形象");
	}

	void BuilderHand(void)
	{
		m_pProduct->add("一个瘦的手臂形象");
	}

	void BuilderFoot(void)
	{
		m_pProduct->add("一个瘦的腿部形象");
	}

	CProudct *GetResult(void)
	{
		return m_pProduct;
	}

private:
	CProudct *m_pProduct;
};

#endif	//#ifndef _CONCRETE_BUILDER_THIN_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName:  ConcreteBuilderFat.h
*Author:  Huangjh
*Version:
*Date:  2017-10-29
*Description:  具体建造者——比较胖的人设
*Others:
**********************************************************************************/
#ifndef _CONCRETE_BUILDER_FAT_H
#define _CONCRETE_BUILDER_FAT_H

#include 
#include "Builder.h"
#include "Product.h"

class CConcreteBuilderFat : public CBuilder
{
public:
	CConcreteBuilderFat()
	{
		try
		{
			m_pProduct = new CProudct();
		}
		catch (std::bad_alloc &ex)
		{
			std::cout << "new CProudct:" << ex.what() << std::endl;
			abort();
		}
	}

	~CConcreteBuilderFat()
	{
		delete m_pProduct;
	}

	void BuilderHead(void)
	{
		m_pProduct->add("一个胖的头部形象");
	}

	void BuilderBody(void)
	{
		m_pProduct->add("一个胖的身体形象");
	}

	void BuilderHand(void)
	{
		m_pProduct->add("一个胖的手臂形象");
	}

	void BuilderFoot(void)
	{
		m_pProduct->add("一个胖的腿部形象");
	}

	CProudct *GetResult(void)
	{
		return m_pProduct;
	}

private:
	CProudct *m_pProduct;
};

#endif	//#ifndef _CONCRETE_BUILDER_FAT_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName:  Director.h
*Author:  Huangjh
*Version:
*Date:  2017-10-29
*Description:  指挥者
*Others:
**********************************************************************************/
#ifndef _DIRECTOR_H
#define _DIRECTOR_H

#include "Builder.h"

class CDirector
{
public:
	CDirector() { }
	~CDirector() { }

	void Construct(CBuilder *pBuilder)
	{
		if (pBuilder)
		{
			pBuilder->BuilderHead();
			pBuilder->BuilderBody();
			pBuilder->BuilderHand();
			pBuilder->BuilderFoot();
		}
	}
};

#endif	//#ifndef _DIRECTOR_H
/*********************************************************************************
*Copyright(C),Your Company
*FileName:  Director.h
*Author:  Huangjh
*Version:
*Date:  2017-10-29
*Description:  创建者模式测试用例
*Others:
**********************************************************************************/
#include "Director.h"
#include "ConcreteBuilderFat.h"
#include "ConcreteBuilderThin.h"

int main(void)
{
	CProudct *pPruduct;
	CDirector *pDirector = new CDirector();
	CBuilder *pThinPeople = new CConcreteBuilderThin();
	CBuilder *pFatPeople = new CConcreteBuilderFat();

	pDirector->Construct(pThinPeople);
	pPruduct = pThinPeople->GetResult();
	pPruduct->show();

	pDirector->Construct(pFatPeople);
	pPruduct = pFatPeople->GetResult();
	pPruduct->show();

	return 0;
}
运行结果如下所示:
产品创建开始---------
一个瘦的头部形象
一个瘦的身体形象
一个瘦的手臂形象
一个瘦的腿部形象
产品创建完成---------
产品创建开始---------
一个胖的头部形象
一个胖的身体形象
一个胖的手臂形象
一个胖的腿部形象
产品创建完成---------

4. 优缺点

4.1 优点

  • 它使你可以改变一个产品的内部表示。Builder对象提供给导向器一个构造产品的抽象接口。该接口使得生成器可以隐藏这个产品的表示和内部结构。它同时也隐藏了该产品实如何装配的。因为产品是通过抽象接口构造的,你在改变该产品的内部表示时所要做的只是定义一个新的生成器。
  • 它将构造代码和表示代码分离。Builder模式通过封装一个复杂对象的创建和表示方式提高了对象的模块性。客户不需要知道定义产品内部的类的所有信息;这些类是不出现在Builder接口中的。每个ConcreteBuilder包含了创建和装配一个特定产品的所有代码。这些代码只需要写一次;然后不同的Director可以复用它以在相同部件集合的基础上构造不同的Product。
  • 它使你可对构造过程进行更精细的控制。Builder模式与一下子就生成产品的创建型模式不同,它是在导向者的控制下一步一步构造产品的。仅当该产品时向导者才从生成器中取回它。因此Builder接口相比其他创建型模式能更好的反应产品的构造过程。这使你可以更精细的控制构建过程,从而能更精细的控制所得产品的内部结构。

4.2 缺点

  • 建造者模式所创建的产品一般具有相似的过程,如果产品之间差异性很大,则不适合使用建造者模式,因此其应用有限。
  • 如果产品内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很复杂。

5. 应用场景

  在以下情况使用Builder模式
  • 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
  • 当构造过程必须允许被构造的对象有不同的表示时。

6. 参考

  • 《大话设计模式》
  • 《设计模式——可复用面向对象软件的基础》

你可能感兴趣的:(设计模式)