重拾设计模式--模板方法模式
文章目录
- 一、模板方法模式概述
- 二、模板方法模式UML图
- 三、优点
- 1代码复用性高
- 2可维护性好
- 3扩展性强
- 四、缺点
- 五、使用场景
- 六、C++ 代码示例1
- 七、 C++ 代码示例2
一、模板方法模式概述
定义:定义一个操作中的算法骨架,而降一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
定义:模板方法模式是一种行为设计模式。它在一个抽象类中定义了一个算法的骨架(模板方法),将一些步骤的实现延迟到子类中。模板方法定义了算法的步骤顺序,子类可以根据自身的需求重写其中的某些步骤,而整体算法的流程结构保持不变。
二、模板方法模式UML图
三、优点
1代码复用性高
在抽象类中定义的模板方法和一些通用步骤可以在多个子类中复用,避免了代码的重复编写。例如,在一个游戏开发中,游戏角色的移动和攻击行为可能有共同的流程结构,通过模板方法模式可以把这个共同的流程提取出来,不同类型的角色(如战士、法师)可以复用这个流程,只需要实现自己特定的移动和攻击方式即可。
2可维护性好
由于算法的结构在抽象类中已经定义好,当需要对整体算法流程进行修改时,只需要在抽象类中修改模板方法的实现,而不需要在每个具体子类中进行修改。比如,在一个文件读取处理的系统中,如果要改变文件读取后的处理流程顺序,只需要在抽象的文件处理类的模板方法中调整步骤顺序,而各个具体文件类型(如文本文件、二进制文件)的处理子类可以保持不变。
3扩展性强
可以很方便地通过创建新的子类来扩展系统功能。新的子类可以选择性地重写模板方法中的步骤,以实现新的行为。例如,在一个图形绘制系统中,已经有了绘制基本图形(如圆形、矩形)的类,当需要添加一种新的图形(如三角形)时,只需要创建一个新的子类,重写绘制图形的具体步骤,而整体的图形绘制流程(如打开画布、选择颜色、绘制图形、关闭画布)可以复用已有的模板方法。
四、缺点
可能会导致类的层次结构复杂:因为需要创建抽象类和多个子类来实现模板方法模式,所以如果设计不当,可能会导致类的层次结构过于复杂,增加代码的理解和维护难度。
不符合开闭原则的部分情况:虽然模板方法模式在一定程度上符合开闭原则(对扩展开放,对修改关闭),但如果要对模板方法本身进行修改,可能会影响到所有的子类。例如,在模板方法中增加或删除一个步骤,可能需要在所有子类中进行相应的调整。
五、使用场景
多个子类有共同的算法流程,但某些步骤的实现细节不同:比如在一个订单处理系统中,线上订单和线下订单的处理流程都包括接收订单、处理支付、安排发货等步骤,但线上订单和线下订单在处理支付和安排发货的具体方式上可能不同。
需要控制子类的扩展行为,保证算法结构的稳定性:例如在一个编译器的语法分析模块中,不同编程语言的语法分析都有一个基本的流程,如词法分析、语法树构建、语义检查等步骤。通过模板方法模式可以定义这个基本流程,让不同编程语言的语法分析子类在这个框架内进行扩展,同时保证整体的语法分析算法结构不变。
六、C++ 代码示例1
以下是一个简单的 C++ 代码示例,模拟一个游戏角色攻击的模板方法模式。假设有一个抽象的 GameCharacter 类,定义了游戏角色攻击的模板方法,还有两个具体的角色类 Warrior 和 Mage,它们分别重写了攻击的具体实现方式。
#include <iostream>
#include <string>
// 抽象游戏角色类
class GameCharacter
{
public:
// 模板方法,定义了攻击的算法流程
void attack()
{
std::cout << "The character is preparing to attack." << std::endl;
performAttack();
std::cout << "The character has finished the attack." << std::endl;
}
protected:
// 抽象方法,由子类实现具体的攻击方式
virtual void performAttack() = 0;
};
// 战士角色类
class Warrior : public GameCharacter
{
protected:
void performAttack() override
{
std::cout << "1" << std::endl;
std::cout << "2" << std::endl;
std::cout << "The warrior swings his sword." << std::endl;
}
};
// 法师角色类
class Mage : public GameCharacter
{
protected:
void performAttack() override
{
std::cout << "The mage casts a spell." << std::endl;
std::cout << "888" << std::endl;
std::cout << "777" << std::endl;
}
};
int main()
{
Warrior warrior;
Mage mage;
std::cout << "Warrior's attack:" << std::endl;
warrior.attack();
std::cout << "Mage's attack:" << std::endl;
mage.attack();
char t;
std::cin>>t;
return 0;
}
在上述代码中:
首先定义了抽象类 GameCharacter,它有一个公共的方法 attack,这就是模板方法。在 attack 方法中,定义了攻击行为的算法流程,包括准备攻击、执行具体的攻击动作(通过调用纯虚函数 performAttack)和完成攻击。performAttack 函数是一个纯虚函数,需要在子类中实现。
然后定义了两个具体的子类 Warrior 和 Mage,它们都继承自 GameCharacter。在这两个子类中,分别重写了 performAttack 函数,实现了战士挥舞剑和法师释放法术的不同攻击方式。
在 main 函数中,创建了 Warrior 和 Mage 类型的对象,并分别调用它们的 attack 方法,这样就会按照模板方法中定义的流程执行攻击行为,每个角色会执行自己特有的攻击动作。
七、 C++ 代码示例2
#include<iostream>
using namespace std;
//抽象类,
//在父类中定义操作的算法骨架,而具体的实现由子类完成
class resume
{
protected:
virtual void setedu(){}
virtual void setage(){}
virtual void setexp(){}
public:
void setinformation()//骨架
{
setedu();
setage();
setexp();
}
};
//子类
class xiaoming:public resume
{
void setedu(){cout<<"清华大学"<<endl;}//具体实现
void setage(){cout<<"19岁"<<endl;}//具体实现
};
class xiaoli:public resume
{
void setedu(){cout<<"北京大学"<<endl;}//具体实现
void setage(){cout<<"10岁"<<endl;}//具体实现
void setexp(){cout<<"腾讯科技"<<endl;}
};
int main()
{
resume *s1 = new xiaoming();
s1->setinformation();
cout<<endl<<endl;
resume *s2 = new xiaoli();
s2->setinformation();
char t;
std::cin>>t;
return 0;
}