模板方法模式:行为型模式。以饭馆吃饭为例,吃饭过程:点餐(粤菜,卤菜)=》食用=》结账(现金,信用卡,微信支付宝),因为这几个步骤是固定的,所以作为样板。
在固定的步骤情况下,通过多态机制在多个子类中对每个步骤的细节进行差异化实现,这就是模板方法能够达到的效果。

模板模式引使用场景

前景知识:
A公司有一个小游戏项目组——开发单机闯关打斗类游戏(类似街机打拳类游戏)。
一个游戏项目组最少需要三名担任不同角色的员工组成:游戏策划,游戏程序,游戏美术。
a) 游戏策划:简称策划 ,负责提出游戏的各种玩法需求,确定游戏中各种数值比如人物(敌人)的生命值、魔法值。
b) 游戏程序:简称程序,需要与游戏策划紧密配合通过代码来实现游戏策划要求的各种游戏功能。
c) 游戏美术:角色设计,道具设计,游戏特效等等。
现在的需求是:游戏中有一个战士角色,也有生命值,魔法值,攻击力;
战士主角:生命值:800,魔法值:200(暂时用不上,保留),攻击力:300 三个属性。
战士技能:也是燃烧技能,该技能可使附近的敌人失去650生命值,同时自身失去100点魔法值。

namespace _nmsp1
{
	class CWarrior
	{
	public:
		CWarrior(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack)
		{}
		
	public:
		void killBurn()
		{
			cout << "释放燃烧技能" << endl;
			m_life -= 100;
		}
	private:
		int m_life;
		int m_magic;
		int m_attack;
	};
}

int main()
{
	_nmsp1::CWarrior w1(1000, 0, 200);
	w1.killBurn();
}

新需求:
原有需求:一个战士角色,有生命值,魔法值,攻击力;
战士主角:生命值:1000,魔法值: 0(暂时用不上,保留),攻击力:300 三个属性。
战士技能:也是燃烧技能,该技能可使附近的敌人失去500生命值,同时自身失去300点魔法值。
新增需求是:新增加一个法师角色,也有生命值,魔法值,攻击力;
法师主角:生命值:800,魔法值:200(暂时用不上,保留),攻击力:300 三个属性。
法师技能:也是燃烧技能,该技能可使附近的敌人失去650生命值,同时自身失去100点魔法值。
代码改进:

// 第二版:重构
namespace _nmsp2
{
	class Fighter
	{
	public:
		Fighter(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack)
		{}
		~Fighter() {}
	public:
		void killBurn()
		{
			effect_enemy();
			effect_self();
			play_effect();
		}

	private:
		virtual void effect_enemy() {}
		virtual void effect_self() {}
		void play_effect() {
			cout << "释放燃烧技能的画面" << endl;
		}
	protected:	// 被子类访问,所以用 protected 修饰
		int m_life;
		int m_magic;
		int m_attack;
	};

	//战士
	class F_Warrior : public Fighter
	{
	public:
		F_Warrior(int life, int magic, int attack) : Fighter(life, magic, attack) {}
	private:
		virtual void effect_enemy() 
		{
			cout << "战士释放技能,让敌人失去500生命值" << endl;
		}
		virtual void effect_self() 
		{
			cout << "战士释放技能,让自己失去300生命值" << endl;
			m_life -= 300;
		}
	};

	// 新增加法师
	class F_Magic : public Fighter
	{
	public:
		F_Magic(int life, int magic, int attack) : Fighter(life, magic, attack) {}
	private:
		virtual void effect_enemy()
		{
			cout << "战士法师技能,让敌人失去650生命值" << endl;
		}
		virtual void effect_self()
		{
			cout << "战士法师技能,让自己失去100生命值" << endl;
			m_life -= 100;
		}
	};

}

int main()
{

	// 创建角色,并释放各自技能
	_nmsp2::Fighter* w1 = new _nmsp2::F_Warrior(1000, 0, 200);
	w1->killBurn();

	_nmsp2::Fighter* w2 = new _nmsp2::F_Magic(800, 200, 300);
	w2->killBurn();
	/*
	战士释放技能,让敌人失去500生命值
	战士释放技能,让自己失去300生命值
	释放燃烧技能的画面
	战士法师技能,让敌人失去650生命值
	战士法师技能,让自己失去100生命值
	释放燃烧技能的画面
	*/
}

引入模板方法(Template Method)模式

在软件开发中需求变化是频繁的,开发人员要尝试寻找变化点,把变化部分和稳定部分分离开来,在变化的地方应用设计模式。
在设计模式中,往往将成员函数说成是算法。
将一个事件中,程序固定的流程称为模板。比如下面的基类中,不论是法师还是战士,要释放燃烧技能,其对敌人影响,对自己影响,播放不同的技能画面,这三个流程就可以抽象成一个固定的流程,这个流程就是一个模板。

在这里插入图片描述

模板方法(Template Method)模式的UML图

在这里插入图片描述
矩形框中:最上边是父类,然后是属性名称,最下边是成员方法。
父类用大字体表示;
成员属性:在第二个位置;
成员方法:固定的模板执行流程方法用大字体表示;
其中,public :+ ; private: - ; protected: #
虚方法:用 斜体表示,写在最后;

程序代码的进一步完善及应用联想

不管是战士还是法师,在释放技能时,要判断是否可以释放,所以要加一个判断的方法,如果可以释放再执行燃烧技能,如果不能释放,就不能执行燃烧技能。
所以这个控制燃烧技能是否可以被释放,应该是一个虚方法,战士要根据血量来判断,法师要根据魔法来判断,实现代码如下:

// 第二版:重构
namespace _nmsp2
{
	class Fighter
	{
	public:
		Fighter(int life, int magic, int attack) :m_life(life), m_magic(magic), m_attack(attack)
		{}
		~Fighter() {}
	public:
		void killBurn()
		{
			if (!isEmit())
			{
				return;
			}
			effect_enemy();
			effect_self();
			play_effect();

		}

	private:
		virtual bool isEmit() = 0;  // 纯虚方法。
		virtual void effect_enemy() {}
		virtual void effect_self() {}
		void play_effect() {
			cout << "释放燃烧技能的画面" << endl;
		}
	protected:	// 被子类访问,所以用 protected 修饰
		int m_life;
		int m_magic;
		int m_attack;
	};

	//战士
	class F_Warrior : public Fighter
	{
	public:
		F_Warrior(int life, int magic, int attack) : Fighter(life, magic, attack) {}
	private:
		virtual void effect_enemy() 
		{
			cout << "战士释放技能,让敌人失去500生命值" << endl;
		}
		virtual void effect_self() 
		{
			cout << "战士释放技能,让自己失去300生命值" << endl;
			m_life -= 300;
		}
		virtual bool isEmit()
		{
			if (m_life < 300)
			{
				return false;
			}
			return true;
			
		}
	};

	// 新增加法师
	class F_Magic : public Fighter
	{
	public:
		F_Magic(int life, int magic, int attack) : Fighter(life, magic, attack) {}
	private:
		virtual void effect_enemy()
		{
			cout << "战士法师技能,让敌人失去650生命值" << endl;
		}
		virtual void effect_self()
		{
			cout << "战士法师技能,让自己失去100生命值" << endl;
			m_life -= 100;
		}
		virtual bool isEmit()
		{
			if (m_magic < 100)
			{
				return false;
			}
			return true;
		}
	};

}

钩子方法:子类可以控制父类中burn方法的是否向下执行,这就是钩子方法。

模板模式应用场景

车间能够生产很多种零件,每条生产线生产不同的零件,生产零件的流程基本相同,只是生产的不同零件过程有略微变换,这就可以使用模板模式。
去饭店吃饭的过程也是模板模式。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐