JavaScript设计模式-----模板方法模式
模板方法模式是一种只需要使用继承就可以实现的非常简单点的模式。
模板方法模式有两部分组成,第一部分是抽象父类,第二部分是具体的实现子类。通常在抽象父类中封装了子类的算法框架,包括实现
一些公共方法以及封装子类中所有的执行顺序。子类通过继承这个抽象类,也继承了整个算法结构,并且可以选择重写父类的方法。
Coffee or Tea
泡咖啡的步骤通常如下:
- 把水煮沸
 - 用沸水冲泡咖啡
 - 把咖啡倒进杯子
 - 加糖和牛奶
 
通过下面这段代码,就可以得到一杯香浓的咖啡:
var Coffee = function() {};
Coffee.prototype.boilWater = function() {
    console.log('把水煮沸');
}
Coffee.prototype.brewCofffeeGriends = function() {
    console.log('用沸水冲泡咖啡');
}
Coffee.prototype.pourInCup = function() {
    console.log('把咖啡倒进杯子');
}
Coffee.prototype.addSugarAndMilk = function() {
    console.log('加糖和牛奶');
}
Coffee.prototype.init = function() {
    this.boilWater();
    this.brewCofffeeGriends();
    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 Beverge = function() {};
Beverge.prototype.boilWater = function() {
    console.log('把水煮沸');
};
Beverge.prototype.brew = function() {};  //空方法,应该由子类重写
Beverge.prototype.pourInCup = function() {}; //空方法,应该由子类重写
Beverge.prototype.addCondiments = function() {}; //空方法,应该由子类重写
Beverge.prototype.init = function() {
    this.boilWater();
    this.brew();
    this.pourInCup();
}
创建Coffee和Tea子类
创建Beverage类的对象对我们来说没有意义,因为不存在真正一种叫饮料的。接下来创建咖啡类和茶类,并让他们继承饮料类:
var Coffee = function() {};
Coffee.prototype = new Beverge();
//重写抽象父类中的一些方法
Coffee.prototype.brew = function() {
    console.log('用沸水冲泡咖啡');
}
Coffee.prototype.pourInCup = function() {
    console.log('把咖啡倒进杯子');
}
Coffee.prototype.addCondiments = function() {
    console.log('加糖和牛奶');
}
var coffee = new Coffee();
coffee.init();
当调用coffee对象的init方法时,由于coffee对象和Coffee构造器的原型上都没有init方法,所以该请求会顺着原型链,被委托给Coffee的“父类”Beverage原型上的init方法。
而Beverage.prototype.init已经规定好了泡饮料的顺序和把水煮沸这一过程。
泡茶的过程类似。
而在泡茶和泡咖啡的过程中,Beverage.prototype.init就是我们讲的模板方法。
而在java中,则可直接通过抽象类来实现这一过程。
JavaScript没有抽象类的解决方案
javascript并没有从语法层面提供对抽象类的支持。
抽象类的第一个作用是隐藏对象的具体类型,由于JavaScript是一门“类型模糊”的语言,所以隐藏对象的类型在JavaScript中并不重要。
另一方面,当在JavaScript中使用原型继承来模拟传统的类继承时,没有编译器帮助我们进行任何形式的检查,也没有办法保证子类重写父类中的“抽象方法”。
下面提供两种解决方案:
- 用鸭子类型模拟接口检查,以确保子类中确实重写了父类的方法。缺点是会带来不必要的复杂性,而且要求程序员主动进行接口检查,要求在业务代码中增加与业务无关的代码。
 - 让Beverage.prototype.brew等方法直接抛出异常,在忘记重写Coffee.prototype.brew方法时在程序运行时得到错误。
Beverage.prototype.brew = function() {
throw new Error('子类必须重写brew方法');
}缺点是得到错误信息的时间点太靠后。
 
钩子方法
通过上述的步骤,制造出来的茶和咖啡已经可以满足大部分客户的需求了,但有一些客户喝咖啡不加调料(糖和牛奶)的。但是Beverage作为父类已经规定好了冲泡饮料的4个
步骤,怎么才能实现某些定制需求呢?
钩子方法(hook)可以解决这个问题,放置钩子是一种隔离变化的常见手段。在父类中容易变化的地方放置钩子,钩子可以有一个默认的实现,究竟要不要“挂钩”,由子类自行决定。
代码做如下调整:
var Beverge = function() {};
Beverge.prototype.boilWater = function() {
    console.log('把水煮沸');
};
Beverge.prototype.brew = function() {
    throw new Error('子类必须重写brew方法');
}; 
Beverge.prototype.pourInCup = function() {
    throw new Error('子类必须重写pourInCup方法');
}; 
Beverge.prototype.addCondiments = function() {
    throw new Error('子类必须重写addCondiments方法');
}; 
Beverge.prototype.init = function() {
    this.boilWater();
    this.brew();
    this.pourInCup();
    if (this.customerWantsCondiments()) {
        this.addCondiments();
    }
}
var CoffeeWithHook = function() {};
CoffeeWithHook.prototype = new Beverge();
//重写抽象父类中的一些方法
CoffeeWithHook.prototype.brew = function() {
    console.log('用沸水冲泡咖啡');
}
CoffeeWithHook.prototype.pourInCup = function() {
    console.log('把咖啡倒进杯子');
}
CoffeeWithHook.prototype.addCondiments = function() {
    console.log('加糖和牛奶');
}
CoffeeWithHook.prototype.customerWantsCondiments = function() {
    return window.confirm('请问需要调料吗');
}
var coffee = new CoffeeWithHook();
coffee.init();
真的需要“继承”吗
模板方法时为数不多的基于继承的设计模式,但JavaScript实际上没有提供真正的类式继承。
由于JavaScript的灵活性,实现这样的例子不一定非继承不可。
在好莱坞原则的指导下,可以这样实现:
var Beverge = 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 = Beverge({
    brew: function() {
        console.log('用沸水冲泡咖啡');
    },
    pourInCup:function() {
        console.log('把咖啡倒进杯子');
    },
    addCondiments: function() {
        console.log('加糖和牛奶');
    }
});
var coffee = new Coffee();
coffee.init();
小结
在JavaScript中,我们很多时候都不需要依样画瓢去实现一个模板方法模式,高阶函数是更好的方式。
JavaScript设计模式-----模板方法模式的更多相关文章
- javascript设计模式——模板方法模式
		
前面的话 在javascript开发中用到继承的场景其实并不是很多,很多时候喜欢用mix-in的方式给对象扩展属性.但这不代表继承在javascript里没有用武之地,虽然没有真正的类和继承机制,但可 ...
 - linkin大话设计模式--模板方法模式
		
linkin大话设计模式--模板方法模式 准备一个抽象类,将部分逻辑以具体方法的形式实现,然后申明一些抽象方法来迫使子类实现剩余的逻辑.不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不 ...
 - 结合JDK源码看设计模式——模板方法模式
		
前言: 相信很多人都听过一个问题:把大象关进冰箱门,需要几步? 第一,把冰箱门打开:第二,把大象放进去:第三,把冰箱门关上.我们可以看见,这个问题的答案回答的很有步骤.接下来我们介绍一种设计模式--模 ...
 - 瑞幸咖啡还是星巴克,一杯下午茶让我明白 设计模式--模板方法模式(Template Method Pattern)
		
简介 Define the skeleton of an algorithm in an operation,deferring some steps to subclasses.Template M ...
 - [设计模式] javascript 之 模板方法模式
		
模板方法模式说明 定义:定义方法操作的骨架,把一些具体实现延伸到子类中去,使用得具体实现不会影响到骨架的行为步骤! 说明:模式方法模式是一个继承跟复用的典型模式,该模式定义了一个抽象类,Abstrac ...
 - C#设计模式-模板方法模式
		
提到模板,大家肯定不免想到生活中的“简历模板”.“论文模板”.“Word中模版文件”等,在现实生活中,模板的概念就是——有一个规定的格式,然后每个人都可以根据自己的需求或情况去更新它,例如简历模板,下 ...
 - java设计模式 模板方法模式Template Method
		
设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性.毫无疑问,设计模式于己 ...
 - javascript 设计模式-----策略模式
		
在<javascript设计模式>中,作者并没有向我们介绍策略模式,然而它却是一种在开发中十分常见的设计模式.最常见的就是当我们遇到一个复杂的表单验证的时候,常常需要编写一大段的if和el ...
 - JAVA 设计模式 模板方法模式
		
定义 模板方法模式 (Template Method) 定义了一个操作中的算法的骨架,而将部分步骤的实现在子类中完成. 模板方法模式使得子类可以不改变一个算法的结构即可重定义该算法的某些特定步骤. 模 ...
 
随机推荐
- regex_replace
			
Regex_iterator方法需要输入一个正则表达式,以及一个用于替换匹配的字符串的格式化字符串:这个格式化的字符串可以通过表的转义序列引用匹配子字符串的部分内容: 转义序列 $n 替换第n个捕获的 ...
 - Docker删除/停止容器
			
应用场景:某个相关的业务需要重启,容器太多了,一个一个通过命令行来关闭太麻烦了,直接一条命令直接搞定. 命令如下: $ docker ps // 查看所有正在运行容器 $ docker stop co ...
 - psql: 致命错误:  对用户"user1"的对等认证失败
			
操作系统:Debian8 登录pg时可能会有提示错误: $ psql -U user1 -d exampledb psql: 致命错误: 对用户"user1"的对等认证失败 打开以 ...
 - WebGl  多缓冲区传递颜色和坐标(矩形)
			
效果: 代码: <!DOCTYPE html> <html lang="en"> <head> <meta charset="U ...
 - jQuery----淘宝商品展示(类似与tab切换)
			
实现效果如图: 功能需求: ①鼠标进入商品名称,商品名称变色,同时对应的物品展示图片显示对应的物品,鼠标移出时候,商品名称恢复原来的颜色 实现分析: 1.HTML+CS ...
 - Cenos6.6 升级 python3.5.2 安装配置 django1.10
			
1 准备编译环境(环境如果不对的话,可能遇到各种问题,比如wget无法下载https链接的文件) yum groupinstall 'Development Tools' yum install zl ...
 - OpenStack入门篇(二十二)之实现阿里云VPC的SDN网络
			
1.修改/etc/neutron/neutron.conf配置 [root@linux-node1 ~]# vim /etc/neutron/neutron.conf [defalut] ... co ...
 - element-UI表格从一列中,拿到当前行的index----scope
			
这里拿到每一行的index----------scope.$index 这里拿到每一行的数据-----------scope.row 转: https://blog.csdn.net/bright20 ...
 - TMS320VC5509的外部中断
			
1. 外部中断引脚INT0-INT4,INT2-平时是低电平,INT3-平时是高电平 2. 不过中断不支持设置上升沿和下降沿触发,中断就是中断,我估计应该是平时是高电平,然后低电平触发中断,代码比较简 ...
 - C++STL学习笔记_(2)deque双端数组知识
			
#include<iostream> using namespace std; #include "deque" #include "algorithm&qu ...