Tamplate Method Pattern
模板方法模式:针对一连串有固定顺序的操作,把共有部分集中到基类中,不同部分分散到不同子类中去实现。C++ 对此还有一种特殊的设计 NVI 。
设计出发点:(1)提高代码复用性, 不同派生类的共同部分集中在基类中(2)基类通过对派生类的具体实现来拓展不同的功能。
生活中的例子:(1)炒菜,炒不同的菜,会有相同的步骤(倒油,热油,装盘等),也会有不同的步骤(加不同的材料,佐料等),在这一连串操作中,把不同的部分分到不同的派生类中实现(2)登录不同系统的过程,有相同的步骤(联网,打开浏览器,关闭浏览器),也有不同的步骤(输入不同网址,不同登录方式等),这一连串操作也可以运用模板方法。
例子:登录不同系统
基类 : loginSystem
(1)定义一些操作的“骨架”(顶层设计),将共同部分集中在基类,供派生类直接调用
(2)为派生类提供接口,将其中一些操作延迟到子类中实现
// 基类, 登录某个系统
class loginSystem {
public:
virtual ~loginSystem() = default;
// ... 特别处理 copy constructor, assignment oeprator, move constructor 等
void loginIn() {
connect(); // 连接校园网
openBrowser(); // 打开浏览器
inputAdress(); // 输入网址
loginMethod(); // 登陆方式
closeBrowser(); // 关闭浏览器
}
static void connect() {
std::cout << "\n连接校园网\n";
}
static void openBrowser() {
std::cout << "\n打开浏览器\n";
}
virtual void inputAdress() = 0; // 接口
virtual void loginMethod() = 0; // 接口
static void closeBrowser() {
std::cout << "\n关闭浏览器\n";
}
} ;
派生类 1 : 登录支付宝
不改变基类的算法结构,重新定义一连串有序操作中的某个或某几个特定操作。(下同)
// 派生类, 登录支付宝
// 登录方式 : 手机验证码
class loginAlipay : public loginSystem {
private:
std::string phoneNumber = "1326176****";
std::string verifyCode = "**0219";
public:
void inputAdress() override {
std::cout << "\n输入 https://www.alipay.com\n";
}
void loginMethod() override {
std::cout << "\n手机号 : ";
std::cin >> phoneNumber;
std::cout << "\n验证码 : ";
std::cin >> verifyCode;
}
} ;
派生类 2 : 登录 QQ 邮箱
// 派生类, 登录 QQ 邮箱
// 登陆方式 : 用户名密码登录
class loginMail : public loginSystem {
private:
std::string userName = "Fluence";
std::string passWord = "YHL";
public:
void inputAdress() override {
std::cout << "\n输入 https://mail.qq.com\n";
}
void loginMethod() override {
std::cout << "\n用户名 : ";
std::cin >> userName;
std::cout << "\n密码 : ";
std::cin >> passWord;
}
} ;
派生类 3: 登录银行账户
// 派生类, 登录银行
// 登陆方式: 指纹识别
class loginBank : public loginSystem {
private:
std::vector<double> fingerPrint;
public:
void inputAdress() override {
std::cout << "\n输入 http://www.****.com/cn/index.html\n";
}
void loginMethod() override {
std::cout << "\n从本地采取指纹 : \n";
std::ifstream statistics("myFingerPrin.txt");
double details;
while(statistics >> details)
fingerPrint.emplace_back(details);
statistics.close(); // 为了简略, 未加入 RAII, 非异常安全
}
} ;
测试代码:
#include <iostream>
#include <fstream>
#include <memory>
#include <string>
#include <vector>
// ..... 以上 class
int main() {
using ptrType = std::shared_ptr<loginSystem>;
ptrType alier = std::make_shared<loginAlipay>();
std::cout << "\n\n--------------- 登录支付宝 ----------------\n";
alier->loginIn();
ptrType qqmailer = std::make_shared<loginMail>();
std::cout << "\n\n--------------- 登录 QQ 邮箱 ----------------\n";
qqmailer->loginIn();
ptrType banker = std::make_shared<loginBank>();
std::cout << "\n\n--------------- 登录银行 ----------------\n";13261
banker->loginIn();
return 0;
}
运行结果:
--------------- 登录支付宝 ----------------
连接校园网
打开浏览器
输入 https://www.alipay.com
手机号 : 13261764268
验证码 : 920219
关闭浏览器
--------------- 登录 QQ 邮箱 ----------------连接校园网
打开浏览器
输入 https://mail.qq.com
用户名 : Fluence
密码 : YHL
关闭浏览器
--------------- 登录银行 ----------------连接校园网
打开浏览器
输入 http://www.****.com/cn/index.html
从本地采取指纹 :
关闭浏览器
学习《Effective C++ 》的过程中看到了 NVI (non-virtual interface) 的概念,开启了我对模板方法的好奇。个人以为,NVI 对于理解模板方法大有裨益。