>[success] # 前端的模板方法模式 ~~~ 1.模板方法模式是一种只需使用继承就可以实现 2.组成第一部分是抽象父类(可以利用es6 或者ts实现),第二部分是具体的实现子类 2.1.抽象父类中封装了子类的算法框架,包括实现一些公共方法以及封装子类中所有 方法的执行顺序。 2.2.子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法 3.'解决问题:' 如我们有一些平行的子类,各个子类之间有一些相同的行为,也有一些不同的行为。如果 相同和不同的行为都混合在各个子类的实现中,说明这些相同的行为会在各个子类中重复出现。 但实际上,相同的行为可以被搬移到另外一个单一的地方 4.'如何实现:' 在模板方法模式中,子类实现中的相同部分被上移到父类中,而将不同的部分留待子类来 实现 ~~~ >[info] ## 案例 ~~~ 1.书中举了一个案例咖啡和茶的案例 ~~~ >[danger] ##### 需要泡咖啡需求 ~~~ 我们的需分为四步: 1.把水煮沸 2.用沸水冲咖啡 3.把咖啡倒进杯子 4.加糖和牛奶 ~~~ ~~~ var Coffee = function(){}; Coffee.prototype.boilWater = function(){ console.log( '把水煮沸' ); }; Coffee.prototype.brewCoffeeGriends = function(){ console.log( '用沸水冲泡咖啡' ); }; Coffee.prototype.pourInCup = function(){ console.log( '把咖啡倒进杯子' ); }; Coffee.prototype.addSugarAndMilk = function(){ console.log( '加糖和牛奶' ); }; Coffee.prototype.init = function(){ this.boilWater(); this.brewCoffeeGriends(); this.pourInCup(); this.addSugarAndMilk(); }; var coffee = new Coffee(); coffee.init(); ~~~ >[danger] ##### 泡柠檬茶 ~~~ 我们的需分为四步: 1.把水煮沸 2.用沸水浸泡茶叶 3.把茶水倒进杯子 4.加柠檬 ~~~ ~~~ var Tea = function(){}; Tea.prototype.boilWater = function(){ console.log( '把水煮沸' ); }; Tea.prototype.steepTeaBag = function(){ console.log( '用沸水浸泡茶叶' ); }; Tea.prototype.pourInCup = function(){ console.log( '把茶水倒进杯子' ); }; Tea.prototype.addLemon = function(){ console.log( '加柠檬' ); }; Tea.prototype.init = function(){ this.boilWater(); this.steepTeaBag(); this.pourInCup(); this.addLemon(); }; var tea = new Tea(); tea.init(); ~~~ >[info] ## 分析 ~~~ 1.上面的步骤有很多都是重复行为的动作,我们将这些重复行为的动作进行抽象 的理解可以得到下面的分析: 1.1.原料不同。一个是咖啡,一个是茶,但我们可以把它们都抽象为'饮料'。 1.2.泡的方式不同。咖啡是冲泡,而茶叶是浸泡,我们可以把它们都抽象为'泡'。 1.3.加入的调料不同。一个是糖和牛奶,一个是柠檬,但我们可以把它们都抽象为'调料'。 2.将需求抽象四个步骤: 2.1.把水煮沸 2.2.用沸水冲泡饮料 2.3.把饮料倒进杯子 2.4.加调料 ~~~ >[danger] ##### 封装一个抽象父类 ~~~ 1.这个抽象父类有了我们刚才抽离出来得模板,指定义方法内容,具体实现让子类去做 ~~~ ~~~ 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(); }; ~~~ >[danger] ##### 泡茶继承(分析模板方法) ~~~ 1.首先概念中描述:抽象父类中封装了子类的算法框架,包括实现一些公共方法以及封装子 类中所有方法的执行顺序,根据这个描述可以分析出模板方法是'Beverage.prototype.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(); ~~~ >[success] # 前端的使用场景 ~~~ 1.模板方法模式常被架构师用于搭建项目的框架,架构师定好了框架的骨架, 程序员继承框架的结构之后,负责往里面填空 2.书中给了一个案例场景: 在 Web 开发中也能找到很多模板方法模式的适用场景,比如我们在构建一系列的 UI 组件, 这些组件的构建过程一般如下所示: (1) 初始化一个 div 容器; (2) 通过 ajax 请求拉取相应的数据; (3) 把数据渲染到 div 容器里面,完成组件的构造; (4) 通知用户组件渲染完毕。 我们看到,任何组件的构建都遵循上面的 4 步,其中第(1)步和第(4)步是相同的。第(2)步不 同的地方只是请求 ajax 的远程地址,第(3)步不同的地方是渲染数据的方式。 于是我们可以把这 4 个步骤都抽象到父类的模板方法里面,父类中还可以顺便提供第(1)步和 第(4)步的具体实现。当子类继承这个父类之后,会重写模板方法里面的第(2)步和第(3)步。 ~~~