以一个购物过程为例。购物过程是,先挑选商品,接着付款,最后离开。在这三个过程中,3个步骤是有序的,不能随意颠倒次序,可以编写一个表示购物过程的抽象类,他拥有三个私有函数 choose(),pay(),leave() 分别表示购物的挑选商品,付款和离开 三个过程,依次由shopping 函数调用。
#include<iostream> using namespace std; class shopping { public: void startshopping() { cout<<"start shopping process"<<endl; choose(); pay(); leave(); } private: void choose(){cout<<"select your favorite items"<<endl;} virtual void pay()=0; virtual void leave(){cout<<"leave"<<endl;} }; class storeshopping:public shopping { private: virtual void pay(){cout<<"payment in cash"<<endl;} }; class webshopping:public shopping { private: virtual void pay(){cout<<"payment in credit card"<<endl;} virtual void leave(){cout<<"logout"<<endl;} }; int main() { shopping *ps=new storeshopping; ps->startshopping(); ps=new webshopping; ps->startshopping(); delete ps; return 0; }
运行结果为
从上面的代码可以看出,store 基类通过shopping 函数规定了购物的过程,至于购物的每个步骤应该做什么,由派生类来指定。由于表示购物过程,的函数被声明为私有的,所以不能通过派生类来调用choose(),pay(),leave(),避免了出现离开,再挑选上平的混乱情况。不论在实体店购物还是在网上购物,都是一样的,因此choose() 被声明为私有非虚函数,这是一个不可更改的实现细节,实体付款可以采用现金来付款,网上购物只能采用信用卡来付款,不同的派生类有不同的实现,因此 pay() 被声明为纯虚函数。leave() 虽然实体店和网店离开方式不同,可以给出一个默认的实现,通过默认实现已经能够清楚表达出离开 派生类可以改写他,也可以不改写。
综上,模板方法定义了一个操作中算法的骨架,而将一些步骤的实现延迟到派生类中,模板方法使得派生类可以不改变一个算法的结构即可重定义算法的某些特定步骤。
class basetemplate { private: void step1(){...} //不可被更改的实现细节 virtual void step2() {...} //可以被派生类修改的实现细节 virtual void step3()=0{...} // 必须被派生类修改的实现细节 public: void work() { step1(); step2(); step3(); } };
可以看出,base 中没有暴露任何虚函数,所有这一切都是通过work() 个非虚函数的public 接口实现的,用一个basetemplate 指针调用 work() 时,表面上是一个非虚函数调用,采用静态绑定,但是这个调用背后隐藏的细节确实多态调用,即 step2,step2 被动态绑定。
可以在模板方法中这样设计
1 如果一个函数作为算法骨架不可变更,的一部分,那么可以将此函数作为基类的私有函数,并且在基类的公共骨架中调用该函数,即该函数可以作为骨架的一个不可更改的实现细节
2 如果一个函数提供了算法骨架某环节的一个默认实现,那么可以考虑将该函数作为基类的私有虚函数,表示派生类可以修改他,也可以不修改它
3 如果作为算法骨架一部分某个函数要求在派生类中拥有不同的实现,那么可以考虑将该函数作为基类的私有纯虚函数,表示派生类必须修改他。