Fork me on GitHub

模版方法


模版方法是一种只需要使用继承就可以实现的非常简单的模式。他由两部分组成,第一部分是抽象父类,第二部分是具体实现子类。通常在抽象父类中封装了子类的算法框架,包括实现一些共用方法以及封装子类所有方法的执行顺序。子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。

Coffee or Tea

假设我们现在要泡一杯咖啡步骤如下:

  • 把水煮沸
  • 用沸水冲泡咖啡
  • 把咖啡倒进杯子
  • 加糖和牛奶

泡茶的步骤:

  • 把水煮沸
  • 用沸水浸泡茶叶
  • 把茶水倒进杯子
  • 加柠檬

经过比较,我们可以发现泡茶和泡咖啡有以下共同点:

  • 原料不同。一个是茶一个是咖啡,但是我们都可以把它们抽象为“饮料”

  • 泡的方式不同。咖啡是冲泡,茶叶是浸泡,我们都可以抽象成“泡”。

  • 加入的调料不同。一个是糖和牛奶,一个是柠檬,我们把它们抽象成“调料”。

所以经过抽象后,无论是泡咖啡还是泡茶我们都是要进行下列步骤:

  • 把水煮沸
  • 用沸水冲泡咖啡
  • 把饮料倒进杯子
  • 加调料
    现在用代码来表示这个抽象过程
var Beverage = function()  {};
Beverage.prototype.boilWater = function() {
    console.log('把水煮沸');
};
Beverage.prototype.brew = function(){}; //空方法,应该由子类重写
Beverage.prototype.pourInCup = function(){}; //空方法,应该由子类重写
Beverage.prototype.addCondiments = function(){}; //空方法,应该由子类重写

Beverage.prototype.init= function(){
    this.boilWater();
    this.brew();
    this.pourInCup();
    this.addCondiments();
}; 

接着就是创建Coffee子类和Tea子类

var Coffee = function() {};
Coffee.prototype = new Beverage();
Coffee.prototype.brew = function() {
    console.log('用沸水冲泡咖啡');
};
Coffee.prototype.pourInCup = function() {
    console.log('把咖啡倒进杯子');
};
Coffee.prototype.addCondiments = function() {
    console.log('加糖加牛奶');
};
var Coffee = new Coffee();
Coffee.init();

Tea类也是类似方法创建,这里不做过多叙述。
在这个案例中,Beverage.prototype.init就是模版方法,它引导子类以何种顺序去执行哪些方法。

钩子方法

以上模版在正常状况是可以起到很好的作用的,但是,假如有某个客人不喜欢加糖的咖啡,有什么办法可以让子类不受这个约束呢?
这时候我们再对代码做一点改动:

var Beverage = function()  {};
Beverage.prototype.boilWater = function() {
    console.log('把水煮沸');
};
Beverage.prototype.brew = function(){
    throw new Error('子类必须重写brew方法');
}; 
Beverage.prototype.pourInCup = function(){
    throw new Error('子类必须重写pourInCup 方法');
};
Beverage.prototype.addCondiments = function(){
    throw new Error('子类必须重写addCondiments 方法');
}; 
Beverage.prototype.customerWantsCondiments = function() {
    return true; //默认需要调料
};
Beverage.prototype.init= function(){
    this.boilWater();
    this.brew();
    this.pourInCup();
    if (this.customerWantsCondiments()) { //如果返回true,则需要调料
        this.addCondiments();
    }

}; 

var CoffeeWithHook = function() {};
CoffeeWithHook.prototype = new Beverage();
CoffeeWithHook.prototype.brew = function() {
    console.log('用沸水冲泡咖啡');
};
CoffeeWithHook.prototype.pourInCup = function() {
    console.log('把咖啡倒进杯子');
};
CoffeeWithHook.prototype.addCondiments = function() {
    console.log('加糖加牛奶');
};
CoffeeWithHook.prototype.customerWantsCondiments = funtion() {
    return window.confirm('请问需要调料吗?');
}
var coffeeWithHook = new CoffeeWithHook();
coffeeWithHook.init();
我知道是不会有人点的,但万一有人想不开呢?