前言
一、“组件协作” 模式
现代软件专业分工之后的第一个结果是 “框架与应用程序的划分”,“组件协作” 模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作时常用的模式。
典型模式
Template Method
Observer / Event
Strategy
二、Template Method模式(模板方法模式)
1、动机
在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因(比如框架与应用之间的关系)而无法和任务的整体结构同时实现。
如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚期实现需求?
2、源码分析讲解
以下展示应用程序开发人员调用程序库开发人员写的库函数接口的两种不同方法的实现。
①、结构化软件设计
template1_lib.cpp
//程序库开发人员 class Library{ public: void Step1(){ //... } void Step3(){ //... } void Step5(){ //... } };
template1_app.cpp
//应用程序开发人员 class Application{ public: bool Step2(){ //... } void Step4(){ //... } }; int main() { Library lib(); Application app(); lib.Step1(); if (app.Step2()){ lib.Step3(); } for (int i = 0; i < 4; i++){ app.Step4(); } lib.Step5(); }
<1>、结构化软件设计流程
<2>、早绑定与晚绑定
结构化软件设计的流程是一种早绑定的写法,Library 写的比 Application 早,写得比较晚的调用实现比较早的程序就叫做早绑定;
②、面向对象软件设计
template2_lib.cpp
//程序库开发人员 class Library{ public: //稳定 template method void Run(){ Step1(); if (Step2()) { //支持变化 ==> 虚函数的多态调用 Step3(); } for (int i = 0; i < 4; i++){ Step4(); //支持变化 ==> 虚函数的多态调用 } Step5(); } virtual ~Library(){ } protected: void Step1() { //稳定 //..... } void Step3() {//稳定 //..... } void Step5() { //稳定 //..... } virtual bool Step2() = 0;//变化 virtual void Step4() =0; //变化 };
template2_app.cpp
//应用程序开发人员 class Application : public Library { protected: virtual bool Step2(){ //... 子类重写实现 } virtual void Step4() { //... 子类重写实现 } }; int main() { Library* pLib=new Application(); lib->Run(); delete pLib; }
<1>、面向对象软件设计流程
<2>、早绑定与晚绑定
面向对象软件设计的流程是一种晚绑定的写法,Library 反过来调用 Application,实现的比较早的调用实现比较晚的就叫做晚绑定;
稳定中有变化,框架不变,里面被调用的函数晚绑定
3、模板方法模式定义
定义一个操作中的算法的骨架 (稳定),而将一些步骤延迟(变化)到子类中。Template Method 使得子类可以不改变(复用)一个算法的结构即可重定义(override 重写)该算法的某些特定步骤。
注:
此处的“骨架”对应于上面的第二种写法中的 Run,“延迟到子类” 的意思就是定义虚函数让子类去实现或重写,就是支持子类来变化。
第二种写法中的模板方法就是 Run,它是相对稳定的,但是它其中又包含了变化(Step2和Step4)。
如果极端地讨论,全部是稳定的或者全部是变化的都不适合使用设计模式。模式应用的核心就是分辨出变化和稳定。
上述面向对象软件设计所实现的方式就满足以上定义。
然而假定所有东西都是稳定的,那么设计模式就没有任何意义了,设计模式最大的作用就是在变化和稳定中间寻找隔离点,然后来分离他们,从而来管理变化,一个正常的软件体系结构一定既有变化又有稳定点。
得到的启发:在模式应用的时候核心是分辨出来软件体系结构中哪些是稳定的、哪些是变化的。
4、结构
AbstractClass 中的 TemplateMethod() 是稳定的,PrimitiveOperationX() 是变化的。设计模式的学习重点就是区分开“稳定”和“变化”的部分。
对应到之前的代码实现:AbstractClass 就是 Library 类;TemplateMethod() 就是Run()方法;PrimitiveOperation1() 对应于 Step2()方法; PrimitiveOperation2() 对应于 Step4() 方法;ConcreteClass就是Application类;
要点总结
Template Method 模式是一种非常基础性的设计模式,在面向对象系统中有着大量的应用。它用最简洁的机制(虚函数的多态性)为很多应用程序框架提供了灵活的扩展点【注:扩展点就是继承+虚函数】,是代码复用方面的基本实现结构。
除了可以灵活应对子步骤的变化外,“不要调用我,让我来调用你”的反向控制结构是Template Method的典型应用。
在具体实现方面,被Template Method 调用的虚方法可以具有实现,也可以没有任何实现(抽象方法、纯虚方法),但一般推荐将它们设置为 protected 方法。