《javascript设计模式与开发实践》阅读笔记(11)—— 模板方法模式

来源:互联网 发布:mysql left join 编辑:程序博客网 时间:2024/05/18 01:49

模板方法模式:

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

 

泡咖啡和泡茶的例子

 

 1     var Coffee = function(){};    //作为构造函数 2     Coffee.prototype.boilWater = function(){ 3         console.log( '把水煮沸' ); 4     }; 5     Coffee.prototype.brewCoffeeGriends = function(){ 6         console.log( '用沸水冲泡咖啡' ); 7     }; 8     Coffee.prototype.pourInCup = function(){ 9         console.log( '把咖啡倒进杯子' );10     };11     Coffee.prototype.addSugarAndMilk = function(){12         console.log( '加糖和牛奶' );13     };14 15     Coffee.prototype.init = function(){16         this.boilWater();             //煮水17         this.brewCoffeeGriends();     //泡咖啡18         this.pourInCup();             //倒进杯子19         this.addSugarAndMilk();       //加糖和牛奶20     };21 22     var coffee = new Coffee();   //实例一个“咖啡”对象23     coffee.init();   //调用24 25 26     var Tea = function(){};    //作为构造函数27     Tea.prototype.boilWater = function(){28         console.log( '把水煮沸' );29     };30     Tea.prototype.steepTeaBag = function(){31         console.log( '用沸水浸泡茶叶' );32     };33     Tea.prototype.pourInCup = function(){34         console.log( '把茶水倒进杯子' );35     };36     Tea.prototype.addLemon = function(){37         console.log( '加柠檬' );38     };39 40     Tea.prototype.init = function(){41         this.boilWater();      //煮水42         this.steepTeaBag();    //泡茶43         this.pourInCup();      //倒进杯子44         this.addLemon();       //加柠檬45     };46 47     var tea = new Tea();  //实例一个“茶”对象48     tea.init();    //调用

 

 

观察两段代码,发现其实他们是大同小异的,那我们完全可以把公共的部分抽象出来,作为一个抽象的父类模板

 

 1     var drink=function(){}  //作为抽象的构造类 2     drink.prototype.boilWater=function(){ 3         console.log( '把水煮沸' ); 4     } 5     drink.prototype.brew=function(){};  //空方法,用来给模板套用 6     drink.prototype.pourInCup = function(){};   // 同上   7     drink.prototype.addCondiments = function(){}; //  同上 8  9     drink.prototype.init = function(){    //模板在这里,重复的事情放在模板里10         this.boilWater();11         this.brew();12         this.pourInCup();13         this.addCondiments();14     };

 

然后就是具体的咖啡类和茶类

 1     /**创建咖啡类**/ 2     var Coffee = function(){};    //这是一个具体的类,作为构造函数存在 3     Coffee.prototype = new drink();   //把构造函数的prototype指向抽象类的实例 4  5     Coffee.prototype.brew = function(){ 6         console.log( '用沸水冲泡咖啡' ); 7     }; 8     Coffee.prototype.pourInCup = function(){ 9         console.log( '把咖啡倒进杯子' );10     };11     Coffee.prototype.addCondiments = function(){12         console.log( '加糖和牛奶' );13     };14 15     var coffee = new Coffee();  //新建咖啡实例16     coffee.init();            //调用模板17     /*结果*/18     //把水煮沸19     //用沸水冲泡咖啡20     //把咖啡倒进杯子21     //加糖和牛奶
 1     /**创建茶类**/ 2     var Tea = function(){};       //具体的类,作为构造函数 3     Tea.prototype = new drink();  //把构造函数的prototype指向抽象类的实例 4  5     Tea.prototype.brew = function(){ 6         console.log( '用沸水浸泡茶叶' ); 7     }; 8     Tea.prototype.pourInCup = function(){ 9         console.log( '把茶倒进杯子' );10     };11     Tea.prototype.addCondiments = function(){12         console.log( '加柠檬' );13     };14 15     var tea = new Tea();   //新建茶实例16     tea.init();     //调用模板17     /*结果*/18     //把水煮沸19     //用沸水浸泡茶叶20     //把茶倒进杯子21     //加柠檬

 

使用es6的话,代码可以简洁不少

 1      class drink{    //抽象模板父类 2         boilWater(){ 3            console.log("把水煮沸"); 4         }; 5         brew(){}; 6         pourInCup(){}; 7         addCondiments(){}; 8  9         init(){10            this.boilWater();11            this.brew();12            this.pourInCup();13            this.addCondiments();14         }15      }16 17      class Coffee extends drink{  //具体的类,内部重写相应方法18         brew(){19            console.log("用沸水冲泡咖啡");20         }21         pourInCup(){22            console.log( '把咖啡倒进杯子' );23         }24         addCondiments(){25            console.log( '加糖和牛奶' );26         }27      }28 29      var coffee=new Coffee(); //实例30      coffee.init();   //把水煮沸31                       //用沸水冲泡咖啡32                       //把咖啡倒进杯子33                       //加糖和牛奶

 

 

js实现模板模式的一些问题

我们在抽象父类中提供了模板,在具体的类中重写相应的方法,但是这个过程全靠程序员的自觉和记忆,语言层面并没有提供任何检查,如果我们忘了重写相应的方法,js也不会报错。
一种解决方案是用鸭子类型来模拟接口检查,缺点就是会带来很多不必要的复杂性,增加很多和业务无关的代码。
另一种解决方案是抽象父类中的相应抽象方法里都抛出错误,如果子类没有重写相应方法,运行时就会报错,如下:

1     drink.prototype.brew=function(){2         throw new Error( '子类必须重写brew 方法' );3     };

 

钩子方法

可以增加自由度,有的子类并不适合把模板方法全部运行,钩子方法可以让子类自行决定是否执行对应的模板方法。

 1     drink.prototype.add=function(){  //钩子方法,子类中可以改写,模板方法中会进行判断 2         return true;   //默认为true,用于判断 3     } 4     drink.prototype.init = function(){  //模板方法   5         this.boilWater(); 6         this.brew(); 7         this.pourInCup(); 8         if( this.add() ){   //如果挂钩返回true,则需要调料 9             this.addCondiments();10         }11     };

 

0 0
原创粉丝点击