首页 > 代码库 > C++设计模式之建造者模式(三)

C++设计模式之建造者模式(三)

4、引入钩子方法的建造者模式

    建造者模式除了逐步构建一个复杂产品对象外,还可以通过Director类来更加精细地控制产品的创建过程,例如增加一类称之为钩子方法(HookMethod)的特殊方法来控制是否对某个buildPartX()的调用,也就是判断产品中某个部件是否需要被建造。钩子方法的返回类型通常为boolean类型,方法名一般为isXXX(),钩子方法定义在抽象建造者类中。在抽象建造者类中提供钩子方法的默认实现,具体建造者类如果不需要建造某个部件,则该建造者类覆盖抽象建造者类的钩子方法。

    暴风影音播放器是具体的产品,实现代码和C++设计模式之建造者模式(一)博客一样,这里就不再呈现。而抽象播放器模式类中定义了一系列的钩子方法,并提供了默认的实现,用于判断是否需要创建对应的部件。如果具体播放器模式不需要某个部件,则具体播放器模式覆盖对应的钩子方法。

    播放模式.h头文件代码如下:

#ifndef _PLAY_PATTERN_H_
#define _PLAY_PATTERN_H_
#include <iostream>
#include <string>
#include "Player.h"
using namespace std;

//抽象播放模式
class PlayPattern
{
protected:
	//具体产品(播放器)
	Player * m_pPlayer;
public:
	PlayPattern()
	{
		m_pPlayer = new Player();
	}

	~PlayPattern()
	{
		if( NULL != m_pPlayer )
		{
			delete m_pPlayer;

			m_pPlayer = NULL;
		}
	}
	
	//制造播放窗口
	virtual void BuildWindow() = 0;

	//制造播放菜单
	virtual void BuildMenu() = 0;

	//制造播放列表
	virtual void BuildPlayList() = 0;

	//制造播放进度条
	virtual void BuildControlBar() = 0;

	//制造收藏列表
	virtual void BuildCollectList() = 0;

	//获取产品(播放器)
	Player * GetPlayer()
	{
		return m_pPlayer;
	}

	//是否建造播放窗口(钩子方法)
	virtual bool IsBuildWindow()
	{
		return true;
	}
	
	//是否建造播放菜单(钩子方法)
	virtual bool IsBuildMenu()
	{
		return true;
	}

	//是否建造播放列表(钩子方法)
	virtual bool IsBuildPlayList()
	{
		return true;
	}

	//是否建造播放进度条(钩子方法)
	virtual bool IsBuildControlBar()
	{
		return true;
	}

	//是否建造收藏列表(钩子方法)
	virtual bool IsBuildCollectList()
	{
		return true;
	}
};


//完整播放模式
class FullPattern : public PlayPattern
{
public:
	void BuildWindow();
	void BuildMenu();
	void BuildPlayList();
	void BuildControlBar();
	void BuildCollectList();

	//完整播放模式不需要建造收藏列表
	bool IsBuildCollectList()
	{
		return false;
	}
};


//精简播放模式
class SimplePattern : public PlayPattern
{
public:
	void BuildWindow();
	void BuildMenu();
	void BuildPlayList();
	void BuildControlBar();
	void BuildCollectList();

	//精简播放模式不需要建造播放菜单
	bool IsBuildMenu()
	{
		return false;
	}

	//精简播放模式不需要建造播放列表
	bool IsBuildPlayList()
	{
		return false;
	}

	//精简播放模式不需要建造收藏列表
	bool IsBuildCollectList()
	{
		return false;
	}
};



//记忆播放模式
class MemoryPattern : public PlayPattern
{
public:
	void BuildWindow();
	void BuildMenu();
	void BuildPlayList();
	void BuildControlBar();
	void BuildCollectList();

	//记忆播放模式不需要建造播放菜单
	bool IsBuildMenu()
	{
		return false;
	}
	
	//记忆播放模式不需要建造播放列表
	bool IsBuildPlayList()
	{
		return false;
	}
};

#endif
    播放器模式Cpp实现代码如下:

#include "PlayPattern.h"

//制造播放窗口
void FullPattern::BuildWindow()
{
	m_pPlayer->SetWindow("主界面窗口");
}


//制造播放菜单
void FullPattern::BuildMenu()
{
	m_pPlayer->SetMenu("主菜单");
}


//制造播放列表
void FullPattern::BuildPlayList()
{
	m_pPlayer->SetPlayList("播放列表");
}


//制造播放进度条
void FullPattern::BuildControlBar()
{
	m_pPlayer->SetControlBar("进度条");
}


//制造收藏列表
void FullPattern::BuildCollectList()
{
	m_pPlayer->SetCollectList(" ");
}


////////////////精简模式///////////////////////////////

void SimplePattern::BuildWindow()
{
	m_pPlayer->SetWindow("主界面窗口");
}


void SimplePattern::BuildMenu()
{
	m_pPlayer->SetMenu(" ");
}


void SimplePattern::BuildPlayList()
{
	m_pPlayer->SetPlayList(" ");
}


void SimplePattern::BuildControlBar()
{
	m_pPlayer->SetControlBar("进度条");
}


void SimplePattern::BuildCollectList()
{
	m_pPlayer->SetCollectList(" ");
}

/////////////////记忆模式////////////////////////////////

void MemoryPattern::BuildWindow()
{
	m_pPlayer->SetWindow("主界面窗口");
}


void MemoryPattern::BuildMenu()
{
	m_pPlayer->SetMenu(" ");
}


void MemoryPattern::BuildPlayList()
{
	m_pPlayer->SetPlayList(" ");
}


void MemoryPattern::BuildControlBar()
{
	m_pPlayer->SetControlBar("进度条");
}

void MemoryPattern::BuildCollectList()
{
	m_pPlayer->SetCollectList("收藏列表");
}

    在暴风影音播放器指导者ContructManage中,调用了一系列的钩子方法,用于判断在不同播放模式下,是否需要创建对应的部件。暴风影音播放器指挥者类.h头文件实现如下:

#ifndef _CONTRUCT_MANAGE_H_
#define _CONTRUCT_MANAGE_H_
#include "PlayPattern.h"
#include "Player.h"

//建造管理器
class ContructManage
{
private:
	//具体建造者
	PlayPattern * m_pPlayPattern;
public:
	//设计播放器模式(也就是设置具体建造者)
	void SetPlayPattern(PlayPattern * pPlayPattern);

	//封装建造过程,调用钩子方法,判断对应的部件是否需要建造
	Player * Construct();
};

#endif
    暴风影音播放器指挥者类Cpp文件实现如下:
#include "ContructManage.h"

//设计播放器模式(也就是设置具体建造者)
void ContructManage::SetPlayPattern(PlayPattern * pPlayPattern)
{
	m_pPlayPattern = pPlayPattern;
}


//封装建造过程,调用一系列钩子方法,判断对应的部件是否需要建造
Player * ContructManage::Construct()
{
	bool bRetVal = true;

	//根据需要建造播放窗口
	bRetVal = m_pPlayPattern->IsBuildWindow();

	if( true == bRetVal )
	{
		m_pPlayPattern->BuildWindow();
	}
	
	//根据需要建造播放菜单
	bRetVal = m_pPlayPattern->IsBuildMenu();

	if( true == bRetVal )
	{
		m_pPlayPattern->BuildMenu();
	}
	
	//根据需要建造播放列表
	bRetVal = m_pPlayPattern->IsBuildPlayList();

	if( true == bRetVal )
	{
		m_pPlayPattern->BuildPlayList();
	}
	
	//根据需要建造播放进度条
	bRetVal = m_pPlayPattern->IsBuildControlBar();

	if( true == bRetVal )
	{
		m_pPlayPattern->BuildControlBar();
	}
	
	//根据需要建造收藏列表
	bRetVal = m_pPlayPattern->IsBuildCollectList();

	if( true == bRetVal )
	{	
		m_pPlayPattern->BuildCollectList();
	}
	
	//返回已经建造好的播放器
	Player * pPlayer = m_pPlayPattern->GetPlayer();

	return pPlayer;
}
   测试程序实现代码如下:

#include <iostream>
#include "ContructManage.h"
#include "PlayPattern.h"
#include "Player.h"

using namespace std;

int main()
{
        /***********************创建建造管理器**********************/
	ContructManage * pContructManage = new ContructManage();
	Player * pPlayer = NULL;

	/***********************完整播放模式************************/
	PlayPattern * pFullPattern = new FullPattern();
	cout << "完整播放模式:" << endl;
	pContructManage->SetPlayPattern(pFullPattern);
	pPlayer = pContructManage->Construct();
	pPlayer->Display();

	/***********************精简播放模式************************/
	PlayPattern * pSimplePattern = new SimplePattern();
	cout << "精简播放模式:" << endl;
	pContructManage->SetPlayPattern(pSimplePattern);
	pPlayer = pContructManage->Construct();
	pPlayer->Display();

	/***********************记忆播放模式************************/
	PlayPattern * pMemoryPattern = new MemoryPattern();
	cout << "记忆播放模式:" << endl;
	pContructManage->SetPlayPattern(pMemoryPattern);
	pPlayer = pContructManage->Construct();
	pPlayer->Display();

	/***********************销毁操作****************************/
	cout << endl;
	delete pFullPattern;
	pFullPattern = NULL;

	delete pSimplePattern;
	pSimplePattern = NULL;

	delete pMemoryPattern;
	pMemoryPattern = NULL;

	delete pContructManage;
	pContructManage = NULL;

	return 0;
}
    编译并执行,结果如下:


    通过引入钩子方法,我们可以在建造者指导者类中对复杂产品的构建进行精细的控制,不仅指定buildPartX()方法的执行顺序,还可以控制是否需要执行某个buildPartX()方法。


5、建造者模式总结

    建造者模式的核心在于如何一步步构建一个包含多个组成部件的完整对象,使用相同的构建过程构建不同的产品,在软件开发中,如果我们需要创建复杂对象并希望系统具备很好的灵活性和可扩展性可以考虑使用建造者模式。

1.主要优点

    建造者模式的主要优点如下:

    (1) 在建造者模式中,客户端不必知道产品内部组成的细节,将产品本身与产品的创建过程解耦,使得相同的创建过程可以创建不同的产品对象。建造者模式封装了产品具体的创建流程,符合"封装变化原则"。

    (2) 每一个具体建造者都相对独立,而与其他的具体建造者无关,因此可以很方便地替换具体建造者或增加新的具体建造者,用户使用不同的具体建造者即可得到不同的产品对象。由于指挥者类针对抽象建造者编程,增加新的具体建造者无须修改原有类库的代码,系统扩展方便,符合“开闭原则”,也符合"针对抽象进行编程而不是针对具体编程原则"。

    (3) 可以更加精细地控制产品的创建过程。将复杂产品的创建步骤分解在不同的方法中,使得创建过程更加清晰,也更方便使用程序来控制创建过程。

2.主要缺点

    建造者模式的主要缺点如下:

    (1) 建造者模式所创建的产品一般具有较多的共同点,其组成部分相似,如果产品之间的差异性很大,例如很多组成部分都不相同,不适合使用建造者模式,因此其使用范围受到一定的限制。

    (2) 如果产品的内部变化复杂,可能会导致需要定义很多具体建造者类来实现这种变化,导致系统变得很庞大,增加系统的理解难度和运行成本。

3.建造者模式的具体应用

    (1)在游戏角色中,存在魔鬼、天使、英雄等角色。这些角色都包含相同的建造过程(建造头、脚、外观等),而每一个游戏角色建造方法各不相同。

    (2)解析XML格式的配置文件时,需要解析配置文件的头部信息、数据体、尾部信息等。可以把解析的三个过程视为建造的三个过程。

    (3)解析Rtf文档格式同样存在和解析XML格式的配置文件相同的情况。

    (4)我们使用Email发送邮件的是否,需要填写邮件标题、收件人、邮件内容等信息。可以把填写邮件标题、收件人、邮件内容视为三个建造过程。

    (5)我们在定义Socket网络通信协议的时候,需要定义数据祯,每祯由包头、包体、包尾组成。这样在通信的双方,就可以按照同样的格式进行收发信息。