现代软件专业分工之后的第一个结果是“框架与应用程序的划分”,“组件协作”模式通过晚期绑定,来实现框架与应用程序之间的松耦合,是二者之间协作时常用的模式。
“组件协作” 模式通常包含
1、模板方法模式
2、观察者模式
3、策略模式
这篇文章中我们首先来了解模板方法模式,其他两个模式在后续的文章中了解熟悉。
只要写过面向对象的程序,用过一些框架,都会使用过模板方法模式,比方QT中实现线程,那么我们一般会编写一个继承QThread的子类,然后在子类中重写run函数,这就是模板方法模式。
动机:
在软件构建过程中,对于某一项任务,它常常有稳定的整体操作结构,但各个子步骤却有很多改变的需求,或者由于固有的原因(比如框架与应用之间的关系)而无法和任务的整体结构同时实现。
如何在确定稳定操作结构的前提下,来灵活应对各个子步骤的变化或者晚期实现需求?
这里重点标注了稳定、变化 两个词,其实在设计模式中,中心思想大部分都是围绕这稳定与变化来展开的,对于稳定的,尽量不去更改,对于有变化需求的,尽量将这些需求与稳定的部分解耦,这就是所谓的提高代码复用与扩展。
为了更好理解上述的话,这里举个例子:请客吃饭
一般我们请客吃饭,常规步骤都是:进饭店 - 点菜 - 吃饭 - 买单 - 离开,这里稳定与变化部分便可以分为:
稳定部分:进饭店、吃饭 、买单、 离开,整个请客步骤(框架)
变化部分:点菜(不同人请不同的人吃饭,可能点的菜不一样)
如果按常规的流程处理,请员工与请老板流程可能如下:
#include <iostream>
using namespace std;
//员工餐
class Employee
{
public:
//进饭店
void enter()
{
cout << "进饭店" <<endl;
}
//点菜
void order()
{
cout << "点盐焗鸡、啤酒鸭、红烧鱼" <<endl;
}
//吃饭
void eat()
{
cout << "吃饭" << endl;
}
//买单
void pay()
{
cout << "买单" << endl;
}
//离开
void leave()
{
cout << "离开" << endl;
}
};
//老板餐
class Boss
{
public:
//进饭店
void enter()
{
cout << "进饭店" <<endl;
}
//点菜
void order()
{
cout << "鱼翅、燕窝、熊掌" <<endl;
}
//吃饭
void eat()
{
cout << "吃饭" << endl;
}
//买单
void pay()
{
cout << "买单" << endl;
}
//离开
void leave()
{
cout << "离开" << endl;
}
};
int main()
{
cout << "----------请员工餐----------" << endl;
Employee e;
e.enter();
e.order();
e.eat();
e.pay();
e.leave();
cout << "----------请老板餐----------" << endl;
Boss b;
b.enter();
b.order();
b.eat();
b.pay();
b.leave();
}
从结果来看,只有点菜部分不一样,其余都一样,包括流程(框架),这种代码是没有复用性,可扩展性也很差,稳定的部分与变动的部分完全耦合到一起,假如要清客户吃饭,那么就要重新实现一遍整个流程,但是实际变化的仅仅是点菜。
为了代码复用与可扩展性,这里我们采用模板方式模式把这个请客系统中,稳定与变化的部分解耦
1、把稳定的部分(进饭店、吃饭 、买单、 离开,整个请客步骤(框架))封装进一个父类(Dinner)中
2、把变化的部分(点菜)在父类中只留一个接口,具体实现放到子类(员工、老板、客户)中完成
结构图为:
#include <iostream>
using namespace std;
//父类(请客)
class Dinner
{
private:
//进饭店
void enter() //稳定部分
{
cout << "进饭店" <<endl;
}
//吃饭
void eat() //稳定部分
{
cout << "吃饭" << endl;
}
//买单
void pay() //稳定部分
{
cout << "买单" << endl;
}
//离开
void leave() //稳定部分
{
cout << "离开" << endl;
}
protected:
virtual void order() = 0; //变化部分在父类中只定义接口,实现由子类实现
public:
void dinner() //稳定部分
{
enter();
order();
eat();
pay();
leave();
}
virtual ~Dinner() //父类的析构函数需要定义成虚函数
{
}
};
//子类(请员工)
class Employee : public Dinner
{
protected:
void order() //子类中实现变动的部分(点菜)
{
cout << "点盐焗鸡、啤酒鸭、红烧鱼" <<endl;
}
};
//子类(请老板)
class Boss : public Dinner
{
protected:
void order() //子类中实现变动的部分(点菜)
{
cout << "鱼翅、燕窝、熊掌" <<endl;
}
};
int main()
{
cout << "----------请员工餐----------" << endl;
Dinner* pE = new Employee();
pE->dinner();
cout << "----------请老板餐----------" << endl;
Dinner* pB = new Boss();
pB->dinner();
}
可以看到,采用模板方式模式重构后,代码的可复用与可扩展性得到了极大提高,假如我们要请客户吃饭,那么只需要定义一个继承自Dinner类的客户子类,并在客户子类中重写order()接口函数即可,调用依旧是调用父类的run()方法。
这个例子大概描述了模板方法模式的实现方式,相信大部分人对这个很熟悉,也经常用到,只是不知道已经用了此模式。
最后再来总结下模板方法模式:
定义一个操作中的算法的骨架 (稳定),而将一些步骤延迟(变化)到子类中。Template Method使得子类可以不改变(复用)一个算法的结构,通过重定义(override 重写)该算法的某些特定步骤,达到框架与业务的解耦。