江湖听闻,在JavaScript的中,往往一个复杂的架构或者库,抽丝剥茧后,大都是设计模式的使用。这次要介绍的模板方法模式由父类和子类构成,通过父类确定整个系统的执行流程,子类负责具体的流程实现,可以通过继承的方式重写父类的某些方法,但是不能改变功流程的执行顺序。体现了抽象与实现分离编程思想。
图中,父类控制了整个系统的执行流程,子类负责具体的流程实现。
一、经典案例饮料冲制流程
我们知道,冲制饮料一般有以下步骤:
(1)把水煮沸
(2)用沸水冲泡饮料
(3)把饮料倒进杯子
(4)加调料
示例代码:
// 父类:实现泡制饮料的子类功能的流程,本次功能有4个流程,如下:
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.init = function () {
this.boilWater();
this.brew();
this.pourInCup();
this.addCondiments();
}
// 子类:具体实现泡制一杯茶的的流程
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()
从以上例子可以看出,父类已经制定了泡制饮料的流程,并且确定了不管哪种饮料都需要把水煮沸的公共方法boilWater,至于brew、pourInCup和addCondiments泡制茶、黑咖啡、牛奶和豆浆等饮料都有所不同,由子类去具体实现。
抽象的父类已经产生,接下来就是泡制茶的子类的具体实现,子类首先继承父类的泡制饮料的确定流程。其中,将水烧开继承父类,brew、pourInCup和addCondiments方法由子类进行重写,至此,泡茶的流程已经完成,黑咖啡、牛奶和豆浆等饮料同理。
以上例子执行结果是:
二.框架案例vue的主流程
vue2.0是最受欢迎的前端框架之一,以其小而美的特点,成为众多前端小伙伴的首选。使用vue的过程中,全局方法的定义、生命周期的使用、组件的封装和路由的实现等都感觉隐隐约约都被一种力量牢牢锁定,vue各个功能在使用的过程有序进行着。翻看vue源码时才发现:
import { initMixin } from './init'
import { stateMixin } from './state'
import { renderMixin } from './render'
import { eventsMixin } from './events'
import { lifecycleMixin } from './lifecycle'
import { warn } from '../util/index'
function Vue (options) {
if (process.env.NODE_ENV !== 'production' &&
!(this instanceof Vue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
// Vue类由各种initMixin、stateMixin、eventsMixin、lifecycleMixin和renderMixin的方法有序的混入各种功能
initMixin(Vue)
stateMixin(Vue)
eventsMixin(Vue)
lifecycleMixin(Vue)
renderMixin(Vue)
export default Vue
我们发现,Vue本质上是一个构造函数,在其new的时候,会执行内部唯一的初始化方法this._init。
初始化方法在initMixin中实现:
Vue.prototype._init = function (options?: Object) {
const vm: Component = this
// ...
initLifecycle(vm)
initEvents(vm)
initRender(vm)
callHook(vm, 'beforeCreate')
initInjections(vm) // resolve injections before data/props
initState(vm)
initProvide(vm) // resolve provide after data/props
callHook(vm, 'created')
// ...
if (vm.$options.el) {
vm.$mount(vm.$options.el)
}
}
}
可以看出,初始化this._init方法是由如图的一些方法确定有序执行的。vue的创建过程中的初始化方法this_init就是一种模板方法模式。
总结
设计模式,百度百科的解释是对面向对象设计中反复出现的问题的解决方案。模板方法模式是众多设计模式之一,解决的主要业务场景是父类创建确定的子类功能或者任务的执行流程,子类继承的时候可以重写父类的某些方法。
参考书籍:《JavaScript设计模式与开发实践》(曾探)
参考代码:cn.vuejs.org