1. 模板方法模式(Template Method Pattern)的定义
(1)定义:定义一个操作中的算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤。
(2)模板方法模式的结构和说明
①AbstractClass:抽象类。用于定义算法骨架和抽象的原语操作,具体的子类通过重定义这些原语操作来实现一个算法的各个步骤。在这个类里面,还可以提供算法中通用的实现。此外,该类还实现了一个模板方法,它定义了算法的骨架。该模板方法不仅调用原语操作,也调用AbstractClass或其他对象中的操作。
②ConcreteClass:具体实现类。用来实现算法骨架中的某些步骤,完成与特定子类相关的功能。
【编程实验】在线购物流程
//行为型模式——模板方式模式 //场景:在线购物 /* 网购,天猫或者京东,经历一下四个步骤: ①访问网站 //访问协议和网址不同 ②浏览下单 //假设两个网站操作一样 ③支付 //天猫:支付宝,东京;货到付款 ④收货 //确认操作一样 注意:两个网站第2步和第4步都是一样的,但访问和支付的访问不一样 */ #include <iostream> #include <string> using namespace std; //抽象模板方法 class OnLineShop { protected: virtual void accessURL() = 0; //由子类去实现 virtual void pay() = 0; //由子类去实现 private: void createOrder() { cout << "创建订单成功" << endl; } void receiptGoods() { cout << "收到了物品" << endl; } public: //购物 void shop() { accessURL(); //第1步:访问网站 createOrder(); //第2步:浏览下单 pay(); //第3步:支付 receiptGoods(); //第4步:收货 } }; //天猫 class Tmall :public OnLineShop { public: void accessURL() { cout << "https://www.tmall.com/" << endl; } void pay() { cout <<"支付宝"<< endl; } }; //京东 class JD :public OnLineShop { public: void accessURL() { cout << "http://www.jd.com/" << endl; } void pay() { cout <<"货到付款"<< endl; } }; int main() { OnLineShop* tmall = new Tmall(); tmall->shop(); cout << endl; OnLineShop* jd = new JD(); jd->shop(); return 0; } /*输出结果: https://www.tmall.com/ 创建订单成功 支付宝 收到了物品 http://www.jd.com/ 创建订单成功 货到付款 收到了物品 */
2. 思考模板方法模式
(1)模板方法模式的本质:固定算法骨架
(2)模板方法模式的核心:处理某个流程的代码己经都具备,但是其中某些节点的代码不能确定。因此,将这些节点的代码实现转移给子类完成。即处理步骤父类中己经定义好,具体实现延迟到子类中定义。
(3)模板方法中的变与不变
①需要变化的地方都通过纯虚函数,把具体实现延迟到子类中。
②不变的部分,进行公共实现。
(4)好莱坞法则:Don’t call me, I’ll call you
①正常的控制结构:子类调用父类方法,这很正常因为子类继承了父类,所以是知道父类的。
②反向的控制结构:父类调用子类方法,因为父类是不可能知道子类的,所以这也是一种反向的控制结构。在C++中是通过虚函数实现的。
3. 模板的写法
(1)模板方法:就是定义算法骨架的方法。如上例中的shop()
(2)具体的操作:在模板中直接实现的某些步骤方法,这些步骤的实现通常是固定的,不怎么会变化的,因此可以将其当作公共功能实现在模板中。如果不需子类访问这些函数,可以定义为private。子类需要访问定义为protected
(3)具体的AbstractClass操作:在模板中实现某些公共功能,可以提供给子类使用,一般不是具体的算法步骤的实现,而是一些辅助的公共功能。
(4)原语操作:就是在模板中定义的纯虚函数,通常是模板方法需要调用的操作,是必须的操作,而且在父类中还没办法确定下来如何实现,需要子类来真正实现的方法。
(5)钩子操作:在模板中定义,并提供默认实现的操作,这些方法通常被视为可扩展的点,但不是必须的,子类可以有选择地覆盖这些方法,以提供新的实现来扩展功能,这些函数一般被声明为virtual。
4. 模板方法的优缺点
(1)优点:实现代码复用。通过把子类的公共功能抽取出来,放到模板中去实现。
(2)缺点:算法骨架不容易升级。模板方法模式最基本的功能是通过模板的制定,把算法骨架完全固定下来。事实上模板和子类是非常耦合的,如果要对模板中的算法骨架进行变更,可能要求所有相关的子类进行相应的变化。所以抽取算法骨架时应尽量确保是不会变化的部分才放到模板中。
5. 模板方法的应用场景
(1)数据库访问的封装
(2)实现一个算法时,整体步骤很固定。但是某些部分易变,这部分可以抽象出来,留给子类去实现。
(3)各个子类中具有公共行为,应该抽取出来,集中在一个公共类中去实现,实现代码复用。
(4)需要控制子类扩展的情况。模板方法模式会在特定的点来调用子类的方法,这样只允许在这些点进行扩展。
6. 相关模式
(1)模板方法和工厂方法
模板方法可以通过工厂方法模式获取需要调用的对象
(2)模板方法模式和策略模式
①从表面上看,两个模式都能实现算法的封装,但模板方法封装的是算法的骨架,这个骨架是不变的,变化的是算法中某些步骤的具体实现;而策略模式是把某个步骤的具体实现算法封装起来,所有封装的算法对象是等价的,可以相互替换。
②可以在模板方法中使用策略模式,就是把那些变化的算法步骤使用实现策略模式来实现,但是具体选择哪个策略还是要由外部来确定,而整体的算法步骤,也就是算法骨架则由模板方法来定义了。