一.定义
模板模式(Template Pattern):一个抽象类公开定义了执行它的方法的方式/模板。它的子类可以按需要重写方法实现,但调用将以抽象类中定义的方式进行。这种类型的设计模式属于行为型模式。
二.Coffee or Tea(模板方法经典案例)
现在我们需要用代码实现冲泡一杯咖啡和一杯茶,不妨分析一下步骤:
经过抽象之后,不管是泡咖啡还是泡茶,我们都能整理为下面四步:
(1) 把水煮沸
(2) 用沸水冲泡饮料
(3) 把饮料倒进杯子
(4) 加调料
/***** 抽象父类 定义泡饮料过程 *******/
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();
};
/***** 子类 泡咖啡类 *******/
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();
/***** 子类 泡茶类 *******/
var Tea = function () { };
Tea.prototype = new Beverage();
Tea.prototype.brew = function () {
console.log('用沸水浸泡茶叶');
};
Tea.prototype.pourInCup = function () {
console.log('把茶倒进杯子');
};
Tea.prototype.addCondiments = function () {
console.log('加柠檬');
};
var tea = new Tea();
tea.init();
三.好莱坞原则
好莱坞无疑是演员的天堂,但好莱坞也有很多找不到工作的新人演员,许多新人演员在好莱坞把简历递给演艺公司之后就只有回家等待电话。有时候该演员等得不耐烦了,给演艺公司打电话询问情况,演艺公司往往这样回答:“不要来找我,我会给你打电话。”
在这一原则的指导下,我们允许底层组件将自己挂钩到高层组件中,而高层组件会决定什么时候、以何种方式去使用这些底层组件,高层组件对待底层组件的方式,跟演艺公司对待新人演员一样,都是“别调用我们,我们会调用你”。
在好莱坞原则的指导之下,我们可以重新实现Coffee or Tea需求。
var Beverage = function (param) {
var boilWater = function () {
console.log('把水煮沸');
};
var brew = param.brew || function () {
throw new Error('必须传递 brew 方法');
};
var pourInCup = param.pourInCup || function () {
throw new Error('必须传递 pourInCup 方法');
};
var addCondiments = param.addCondiments || function () {
throw new Error('必须传递 addCondiments 方法');
};
var F = function () { };
F.prototype.init = function () {
boilWater();
brew();
pourInCup();
addCondiments();
};
return F;
};
var Coffee = Beverage({
brew: function () {
console.log('用沸水冲泡咖啡');
},
pourInCup: function () {
console.log('把咖啡倒进杯子');
},
addCondiments: function () {
console.log('加糖和牛奶');
}
});
var Tea = Beverage({
brew: function () {
console.log('用沸水浸泡茶叶');
},
pourInCup: function () {
console.log('把茶倒进杯子');
},
addCondiments: function () {
console.log('加柠檬');
}
});
var coffee = new Coffee();
coffee.init();
var tea = new Tea();
tea.init();
四.总结
模板方法模式是一种典型的通过封装变化提高系统扩展性的设计模式。在传统的面向对象语言中,一个运用了模板方法模式的程序中,子类的方法种类和执行顺序都是不变的,所以我们把这部分逻辑抽象到父类的模板方法里面。而子类的方法具体怎么实现则是可变的,于是我们把这部分变化的逻辑封装到子类中。通过增加新的子类,我们便能给系统增加新的功能,并不需要改动抽象父类以及其他子类,这也是符合开放封闭原则的。
但在 JavaScript 中,我们很多时候都不需要依样画瓢地去实现一个模版方法模式,高阶函数是更好的选择。