模板方法模式:

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

泡咖啡和泡茶的例子

     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(); //调用 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(); //调用

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

     var drink=function(){}  //作为抽象的构造类
drink.prototype.boilWater=function(){
console.log( '把水煮沸' );
}
drink.prototype.brew=function(){}; //空方法,用来给模板套用
drink.prototype.pourInCup = function(){}; // 同上
drink.prototype.addCondiments = function(){}; // 同上 drink.prototype.init = function(){ //模板在这里,重复的事情放在模板里
this.boilWater();
this.brew();
this.pourInCup();
this.addCondiments();
};

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

     /**创建咖啡类**/
var Coffee = function(){}; //这是一个具体的类,作为构造函数存在
Coffee.prototype = new drink(); //把构造函数的prototype指向抽象类的实例 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 drink(); //把构造函数的prototype指向抽象类的实例 Tea.prototype.brew = function(){
console.log( '用沸水浸泡茶叶' );
};
Tea.prototype.pourInCup = function(){
console.log( '把茶倒进杯子' );
};
Tea.prototype.addCondiments = function(){
console.log( '加柠檬' );
}; var tea = new Tea(); //新建茶实例
tea.init(); //调用模板
/*结果*/
//把水煮沸
//用沸水浸泡茶叶
//把茶倒进杯子
//加柠檬

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

      class drink{    //抽象模板父类
boilWater(){
console.log("把水煮沸");
};
brew(){};
pourInCup(){};
addCondiments(){}; init(){
this.boilWater();
this.brew();
this.pourInCup();
this.addCondiments();
}
} class Coffee extends drink{ //具体的类,内部重写相应方法
brew(){
console.log("用沸水冲泡咖啡");
}
pourInCup(){
console.log( '把咖啡倒进杯子' );
}
addCondiments(){
console.log( '加糖和牛奶' );
}
} var coffee=new Coffee(); //实例
coffee.init(); //把水煮沸
//用沸水冲泡咖啡
//把咖啡倒进杯子
//加糖和牛奶

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

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

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

钩子方法

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

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

javascript设计模式与开发实践阅读笔记(11)—— 模板方法模式的更多相关文章

  1. javascript设计模式与开发实践阅读笔记(4)——单例模式

    定义 单例模式:保证一个类仅有一个实例,并提供一个访问它的全局访问点. 具体来说,就是保证有些对象有且只有一个,比如线程池.全局缓存.浏览器中的window 对象等.在js中单例模式用途很广,比如登录 ...

  2. javascript设计模式与开发实践阅读笔记(8)——观察者模式

    发布-订阅模式,也叫观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知. 在JavaScript开发中,我们一般用事件模型来替代传统的观察者模式. ...

  3. javascript设计模式与开发实践阅读笔记(7)——迭代器模式

    迭代器模式:指提供一种方法顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示. 迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使用迭代器模式之后,即使不关心对象的内部构造,也可以按顺 ...

  4. javascript设计模式与开发实践阅读笔记(6)——代理模式

    代理模式:是为一个对象提供一个代用品或占位符,以便控制对它的访问. 代理模式的关键是,当客户不方便直接访问一个对象或者不满足需要的时候,提供一个替身对象来控制对这个对象的访问,客户实际上访问的是替身对 ...

  5. javascript设计模式与开发实践阅读笔记(5)——策略模式

    策略模式:定义一系列的算法,把它们一个个封装起来,并且使它们可以相互替换. 我的理解就是把各种方法封装成函数,同时存在一个可以调用这些方法的公共函数.这样做的好处是可以消化掉内部的分支判断,使代码效率 ...

  6. javascript设计模式与开发实践阅读笔记(9)——命令模式

    命令模式:有时候需要向某些对象发送请求,但是并不知道请求的接收者是谁,也不知道被请求的操作是什么,此时希望用一种松耦合的方式来设计软件,使得请求发送者和请求接收者能够消除彼此之间的耦合关系. 说法很复 ...

  7. JavaScript设计模式与开发实践——读书笔记1.高阶函数(上)

    说来惭愧,4个多月未更新了.4月份以后就开始忙起来了,论文.毕设.毕业旅行等七七八八的事情占据了很多时间,毕业之后开始忙碌的工作,这期间一直想写博客,但是一直没能静下心写.这段时间在看<Java ...

  8. 《JavaScript设计模式与开发实践》笔记第八章 发布-订阅模式

    第八章 发布-订阅模式 发布-订阅模式描述 发布-订阅模式又叫观察者模式,它定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知. 发布-订阅模式可以广泛应用于 ...

  9. JavaScript设计模式与开发实践——读书笔记1.高阶函数(下)

    上部分主要介绍高阶函数的常见形式,本部分将着重介绍高阶函数的高级应用. 1.currying currying指的是函数柯里化,又称部分求值.一个currying的函数会先接受一些参数,但不立即求值, ...

随机推荐

  1. tt程序分析(一)

    首先是loginactivity login成功以后,跳转到mainActivity. mainActivity中有四个fragment , 聊天        fragment_chat 通讯录   ...

  2. WIN32动态链接库设计与使用

    WINDOWS动态链接库技术能很好地实现代码的分模块,综合来说,windows动态链接库分为三种WIN32动态链接库,使用WINDOWS api函数调用设计,贴近底层,体积小,是最初Windows程序 ...

  3. matlab unique 顺序不变

    对于一个向量,使用unique去重后会自动排序,为了保持原顺序: A = [5,1,8,5,2,8,3,9,6,1];[i,j] = unique(A,'first');B = A(sort(j)); ...

  4. Incompatible pointer types assigning to 'id<>' from 'Class'错误

    实例变量在类方法中被使用 原因:静态方法访问了非静态变量属性(.h中声明的那些属性),就是类方法访问了成员变量

  5. FastJson的用法

    一.简单数据的序列化 pubic class UserInfo implements Serializable{ private String name; private int age; publi ...

  6. Nodejs之使用session

    nodejs中使用session的说明. session介绍 为什么使用session: session运行在服务器端,当客户端第一次访问服务器时,可以将客户的登陆信息保存. 当客户访问其他界面时,可 ...

  7. 实时 Django 终于来了 —— Django Channels 入门指南

    Reference: http://www.oschina.net/translate/in_deep_with_django_channels_the_future_of_real_time_app ...

  8. zip-auto.sh

    #!/bin/sh #auto zip package #Define Path #####test######### mkdir -p /root/shell/test1 /root/shell/t ...

  9. Win10系统安装Tomcat8

    1.JDK 1.8 安装 1.1.在网站上http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151. ...

  10. JAVA 遍历文件夹下的所有文件(递归调用和非递归调用)

    JAVA 遍历文件夹下的所有文件(递归调用和非递归调用) 1.不使用递归的方法调用. public void traverseFolder1(String path) { int fileNum = ...