模板方法模式学习
一、定义和组成
模板方法模式有两部分组成
- 抽象父类
- 具体实现的子类
通常是在父类中定义算法骨架和一些公共的方法,子类继承父类方法的基础上,不修改算法骨架,重新定义父类中算法的某些实现步骤。
二、应用场景
- 多个子类拥有公共的方法,且逻辑相同,可以提取到父类中实现
- 父类实现不变的部分,并将可变行为交给子类来实现
- 需要通过子类来决定父类中某个步骤是否执行,实现子类对父类的反控制
三、例子讲解
我们去银行取钱、存钱、办卡等业务的时候,都需要三大步骤:
- 排队取号;
- 办理具体的业务;
- 服务评价。
然后这个步骤可以抽取到父类中去实现整体的步骤(算法骨架),其中第一步排队取号和第三步服务评价是公共的方法,可以在父类中实现,第二步办理具体业务在不同的子类来实现
3.1 存钱
首先我们来看下银行存钱业务,一般步骤如下:
- 排队取号;
- 存钱;
- 服务评价。
通过以下代码,我们就完成了去银行办理存钱的业务
var Deposit = function () {};
Deposit.prototype.takeNumber = function() {
console.log('排队取号');
};
Deposit.prototype.depositMoney = function() {
console.log('存钱');
};
Deposit.prototype.serviceEvaluation = function() {
console.log('服务评价');
};
Deposit.prototype.init = function() {
this.takeNumber();
this.depositMoney();
this.serviceEvaluation();
console.log('完成银行业务办理');
};
var deposit = new Deposit();
deposit.init();
3.2 取钱
接下来是去银行取钱业务(当然现在都是大笔业务才需要柜台,小笔金额可以直接在ATM操作哈~~),一般步骤如下:
- 排队取号;
- 取钱;
- 服务评价。
通过以下的代码,我们就完成了去银行办理取钱的业务
var Withdraw = function () {};
Withdraw.prototype.takeNumber = function() {
console.log('排队取号');
};
Withdraw.prototype.withdrawMoney = function() {
console.log('取钱');
};
Withdraw.prototype.serviceEvaluation = function() {
console.log('服务评价');
};
Withdraw.prototype.init = function() {
this.takeNumber();
this.withdrawMoney();
this.serviceEvaluation();
console.log('完成银行业务办理');
};
var withdraw = new Withdraw();
withdraw.init();
3.3 分离共同点
现在我们分别完成了存钱和取钱的银行业务,通过思考和比较,我们发现去银行办理业务的流程是大同小异的,如
| 存钱 | 取钱 |
|---|---|
| 排队取号 | 排队取号 |
| 存钱 | 取钱 |
| 服务评价 | 服务评价 |
通过对比发现以下不同点:
- 办理的业务不同。
不管是取钱还是存钱,我们都可以把它抽象为银行业务。那么整体的业务流程步骤大概是以下三步:
- 排队取号;
- 办理银行业务;
- 服务评价。
我们创建一个父类银行业务类来处理银行业务办理的整个过程,不管是取钱还是存钱或者是办卡,我们都用handleBusiness的方法来表示,具体代码如下:
var BankBusiness = function () {};
BankBusiness.prototype.takeNumber = function() {
console.log('排队取号');
};
BankBusiness.prototype.handleBusiness = function() {
// 空方法 由子类重写
};
BankBusiness.prototype.serviceEvaluation = function() {
console.log('服务评价');
};
BankBusiness.prototype.init = function() {
this.takeNumber();
this.handleBusiness();
this.serviceEvaluation();
console.log('完成银行业务办理');
};
创建存钱子类,并让它继承银行业务类:
var Deposit = function () {};
Deposit.prototype = new BankBusiness();
接下来要重写抽象父类中的一些方法,排队取号和服务评价可以直接使用父类BankBusiness中的takeNumber方法和serviceEvaluation方法,handleBusiness方法都需要在Deposit子类中重写,代码如下:
Deposit.prototype.handleBusiness = function () {
console.log('存钱');
}
var deposit = new Deposit();
deposit.init();
至此我们的Deposit类已经完成了,当调用deposit对象的init方法时,由于deposit对象和Deposit构造器的原型prototype上都没有对应的init方法,所以该请求会顺着原型链,被委托给Deposite的“父类”BankBusiness原型上的init方法。而BankBusiness.prototype.init方法中已经规定好了银行办理业务的顺序,所以我们能成功地办理取钱业务,代码如下:
BankBusiness.prototype.init = function() {
this.takeNumber();
this.handleBusiness();
this.serviceEvaluation();
console.log('完成银行业务办理');
};
通过上面的例子,我们可以得知BankBusiness.prototype.init就是我们说的模板方法。 BankBusiness.prototype.init被称为模板方法的原因是,该方法中封装了子类的算法框架,它作为一个算法的模板,指导子类以何种顺序去执行哪些方法。BankBusiness.prototype.init方法中,算法内的每一个步骤都清楚地展示在我们眼前。