介绍
模板方法模式应用比较广泛,可以说它是继承应用的一个基础。有一些情况下,我们实现各种相似的类。不用继承的笨办法就是为分别定义不同的类。这样做的成本比较大,一个简单的办法是,我们可以把不同类中相同部分剥离出来声明一个基类,然后分别继承再实现。这样做确实不错,但是一定程度上增加了耦合。
模板方法模式需要我们创建一个模板(基类),然后根据这个模板生成不同的实现。具体点来说,就是不同的类要继承一个抽象类,这个抽象类中定义了不同的接口。派生类需要实现这些接口。
实现
给模板方法模式下一个具体的定义:父类中定义处理流程的框架,子类中实现具体处理。
UML图如下,图源《图解设计模式》:

需求
现在有如下需求:在控制台中输出文本,输出规则是:
第一行输出现在的类名
第二到六行输出类中的文本
第七行输出现在的类名+end
要求实现两个这样的类,分别处理string和char。
实现模板
看到这个需求,发现流程处理是一样的,只是数据类型不一样。我们可以先定义一个抽象类,其中的display函数包含了处理的流程:
class AbstractDisplay {
protected:
virtual void open() = 0;
virtual void print() = 0;
virtual void end() = 0;
public:
// 处理流程
void display() {
open();
for (int i = 0; i < 5; ++i) {
print();
}
end();
}
};模板中我们定义了接口和处理的流程。接下来我们需要在子类中实现。
具体实现
class CharDisplay : public AbstractDisplay {
public:
CharDisplay(char c) : c(c) {}
protected:
void open() override {
std::cout << "CharDisplay" << std::endl;
}
void print() override {
std::cout << c << std::endl;
}
void end() override {
std::cout << "CharDisplay end" << std::endl;
}
private:
char c;
};
class StringDisplay : public AbstractDisplay {
public:
StringDisplay(std::string c) : c(c) {}
protected:
void open() override {
std::cout << "StringDisplay" << std::endl;
}
void print() override {
std::cout << c << std::endl;
}
void end() override {
std::cout << "StringDisplay end" << std::endl;
}
private:
std::string c;
};子类中我们无需关心业务的处理流程是怎样,只需要将基类中定义的接口实现即可。
尝试运行
在main函数实例化这些类,并调用display方法,结果满足需求。
int main()
{
CharDisplay obj1('a');
StringDisplay obj2("hello");
obj1.display();
obj2.display();
}输出:
CharDisplay
a
a
a
a
a
CharDisplay end
StringDisplay
hello
hello
hello
hello
hello
StringDisplay end扩展
C++中有没有更加便捷的方式实现上面的需求呢?答案是类模板。
template<typename T>
class GeneralDisplay
{
public:
GeneralDisplay<T>(T val) : val(val) {}
void display() {
open();
for (int i = 0; i < 5; ++i) {
print();
}
end();
}
private:
T val;
void open() {
std::cout << "GeneralDisplay" << std::endl;
}
void print() {
std::cout << val << std::endl;
}
void end() {
std::cout << "GeneralDisplay end" << std::endl;
}
};
int main()
{
GeneralDisplay<std::string> obj("hello");
obj.display();
}
评论区