模板模式可以说的是最简单的模式了(跟单例模式一样简单,呵呵)。在面向对象系统分析与设计中通常会遇到这样一种情况:对于某一种业务逻辑(算法实现)在不同的对象中有不同的细节实现,但是逻辑框架是相同的。而template模式则提供了这种情况的一个实现框架。模板模式实现原理也很简单,利用面向对象的继承和多态特性就可以得到。我们平时在编写代码时可能就有用到,比如业务单板有许多种,CHSTM, E1,CEP,ATM等各种不同类型单板。每种单版初始化流程有相同的,也有不一致的,我们可以把相同的部分(固定的部分)放入到父类中,不同的初始化部分在父接口中也提供同一接口,而把具体实现延迟到子类中。模板模式满足面向对象设计的六大原则之依赖倒置原则。什么是倒置?一般我们的程序想到的都是有底层向高层拓展,实现某个功能时,先写好底层的各类API,然后再此基础上构建复合功能。这其实跟修房子一样,一般都想到的是,准备做房子的原料(钢筋,水泥,砖块,沙子)之后,再此基础上修打地基,然后一层层从底楼层向高楼层迈进。但是在软件设计中,所谓顶层设计就是说高层(一般对应抽象类或接口)决定了软件设计模型与框架,先打好框架,然后缺什么再去实现什么。也拿做房子来说,先把整个房子设计图准备好,(一般来说,房子框架一旦决定,就必须得遵守,这也是种接口契约精神,然后按照图纸,缺啥买啥,万一有一天主人说把房子换成木房子,那么只需要把房子某些原料代替了,房子本身的样貌(图纸没变)还是一样),这就是高层决定了底层,底层依赖高层。依赖倒置的意思是,不要让底层去决定高层,而是让高层来决定底层。软件领域的依赖导致原因实质上指的是面向接口编程而不面向实现编程。而模板模式正式体现了这一设计原则。
拿做菜来说,做黄瓜与做青菜可能都会经过这么几个阶段,洗菜,切菜,做菜等过程,下面是C++代码实现,也很简单(以前就实现过,只是不知道这就是模板模式,有木有?)
#include <iostream>
using namespace std;
class AbstractDish
{
public:
virtual void makeDish(void)
{
this->washDish();
this->cutDish();
this->cookDish();
}
protected:
virtual void washDish () = 0;
virtual void cutDish() = 0;
virtual void cookDish() = 0;
};
class CucumberDish:public AbstractDish
{
protected:
void washDish()
{
cout << "洗黄瓜"<<endl;
}
void cutDish()
{
cout << "黄瓜切片"<<endl;
}
void cookDish()
{
cout << "凉拌黄瓜"<<endl;
}
};
class GreenVegDish:public AbstractDish
{
protected:
void washDish()
{
cout << "洗青菜"<<endl;
}
void cutDish()
{
cout << "去青菜叶子"<<endl;
}
void cookDish()
{
cout << "炒青菜"<<endl;
}
};
class Client //场景类
{
public:
void invoker(void)
{
AbstractDish *cucumber = new CucumberDish;
AbstractDish *vegetables = new GreenVegDish;
cucumber->makeDish();
vegetables->makeDish();
}
};
int main()
{
Client client;
client.invoker();
return 0;
}
同时,我们还可以在父类中加入一个钩子方法,由子类的返回结果对父类公共部分产生影响(代码节选自设计模式之禅 ): 悍马型号有2种,H1型与H2型,客户提出H1型号的悍马喇叭想让它响就响,H2型号的喇叭不要有声音。这时我们可以在父类抽象方法中新增一个钩子函数,确定各个型号悍马是否需要声音,由各个实现类覆盖该方法,同时其他的基本方法由于不需要对外提供访问,因此也设计为protected类型,其源代码如下所示:
#include <iostream>
#include <stdio.h>
using namespace std;
class HummerModel
{
public:
virtual void run()
{ this->start();
this->engineBoom();
if (this->isAlarm())
this->alarm();
this->stop();
}
protected:
virtual void start() = 0;
virtual void stop() = 0;
virtual void alarm() = 0;
virtual void engineBoom() = 0;
virtual bool isAlarm(){return true;} -----钩子方法,默认为发声,由子类决定是 否发出声音
};
class HummerH1Model:public HummerModel
{
public:
HummerH1Model():alarmFlag(true) { }
void alarm() { cout << "悍马H1鸣笛" << endl;}
void engineBoom() { cout << "悍马H1引擎声音轰轰响" << endl;}
void start() { cout<< "悍马H1发动" << endl;}
void stop() { cout << "悍马H1停车" << endl;}
void setAlarm(bool isAlarm) { this->alarmFlag = isAlarm;}
private:
bool alarmFlag;
};
class HummerH2Model:public HummerModel
{
public:
void alarm() { cout << "悍马H2鸣笛" << endl;}
void engineBoom() { cout << "悍马H2引擎声音是这样轰隆隆" << endl;}
void start() { cout << "悍马H2发动" << endl;}
void stop() { cout << "悍马H2停车" << endl;}
bool isAlarm() { return false;}
};
class Client
{
public:
void invoker(void)
{
int type = 0;
HummerH1Model *h1 = new HummerH1Model;
cout << "H1型号悍马" << endl;
cout << "H1型号的悍马是否需要喇叭声响?0--不需要 1--需要" << endl;
scanf("%d", &type);
if(0 == type)
{
h1->setAlarm(false);
}
h1->run();
HummerH2Model *h2 = new HummerH2Model;
h2->run();
}
};
int main()
{
Client client;
client.invoker();
return 0;
}
#include <iostream>
#include <stdio.h>
using namespace std;
class HummerModel
{
public:
virtual void run()
{ this->start();
this->engineBoom();
if (this->isAlarm())
this->alarm();
this->stop();
}
protected:
virtual void start() = 0;
virtual void stop() = 0;
virtual void alarm() = 0;
virtual void engineBoom() = 0;
virtual bool isAlarm(){return true;} -----钩子方法,默认为发声,由子类决定是 否发出声音
};
class HummerH1Model:public HummerModel
{
public:
HummerH1Model():alarmFlag(true) { }
void alarm() { cout << "悍马H1鸣笛" << endl;}
void engineBoom() { cout << "悍马H1引擎声音轰轰响" << endl;}
void start() { cout<< "悍马H1发动" << endl;}
void stop() { cout << "悍马H1停车" << endl;}
void setAlarm(bool isAlarm) { this->alarmFlag = isAlarm;}
private:
bool alarmFlag;
};
class HummerH2Model:public HummerModel
{
public:
void alarm() { cout << "悍马H2鸣笛" << endl;}
void engineBoom() { cout << "悍马H2引擎声音是这样轰隆隆" << endl;}
void start() { cout << "悍马H2发动" << endl;}
void stop() { cout << "悍马H2停车" << endl;}
bool isAlarm() { return false;}
};
class Client
{
public:
void invoker(void)
{
int type = 0;
HummerH1Model *h1 = new HummerH1Model;
cout << "H1型号悍马" << endl;
cout << "H1型号的悍马是否需要喇叭声响?0--不需要 1--需要" << endl;
scanf("%d", &type);
if(0 == type)
{
h1->setAlarm(false);
}
h1->run();
HummerH2Model *h2 = new HummerH2Model;
h2->run();
}
};
int main()
{
Client client;
client.invoker();
return 0;
}
可以看到,H1型号的悍马是由客户自己控制是否要响喇叭,也就是说外界条件改变,影响到模板方法的执行,在我们的抽象类中isAlarm的返回值就是影响了模板方法的执行结果,该方法就叫做钩子方法(Hook Method)。有了钩子方法,就可以由子类的一个方法返回值决定公共部分的执行结果。再回到我们的做菜系列,我们也可以在父类中加入个钩子方法isCooked(),默认为true,表示所有菜都是需要煮熟或烹饪的,这样子类可以选择false,改变父类的执行流程,比如黄瓜子类中,可以返回false,表示洗干净就可以食用了,不需要cook,就这么简单!