16种JavaScript设计模式(下)
## 简介
紧接[上文(地址)](https://www.jianshu.com/p/993027963b60),我们继续介绍以下几种设计模式
* 模板方法模式
* 享元模式
* 责任链模式
* 中介者模式
* 装饰者模式
* 状态模式
* 适配器模式
#### 模板方法模式
定义:基于继承的设计模式,在父类中定义需要实现的方法,并定义好方法的执行逻辑,子类只需实现对应方法即可
简介:模板方法模式是一种只需通过继承就可以实现的简单模式。这种模式在前端框架中十分常见,vue、react的组件定义都用到了该模式。大家思考下为什么我们在vue组件中定义的生命周期函数会按照给定的顺序执行呢?通过下面的简单示例来了解下。
例:
```
var Vue = function (config) { // 父类
this.config = config
}
// 定义需要实现的方法
Vue.prototype.created = function () {}
Vue.prototype.mounted = function () {}
Vue.prototype.destoryed = function () {}
// 定义执行逻辑
Vue.prototype.init = function () {
this.created()
this.mounted()
this.destoryed()
}
var Button = function (config) {
Vue.call(this, config)
}
Button.prototype = new Vue()
Button.prototype.created = function () {
console.log('button created')
}
Button.prototype.mounted = function () {
console.log('button mounted')
}
Button.prototype.destoryed = function () {
console.log('button destoryed')
}
var button = new Button()
```
#### 享元模式
定义:将对象属性划分为内、外两种属性,共享内部状态节约内存
简介:享元模式是一种用于性能优化的模式,避免创建大量类似的对象,占用大量内存。
例:
```
var Hero = function (name) {
this.name = name // 内部状态
}
Hero.prototype.show = function () {
console.log('我', this.name, '的新皮肤-', this.skin, '可真好看')
}
var GayLun = new Hero('盖伦')
var skins = ['乞丐装', '官人装', '皇帝装']
for (var index = 0; index < skins.length; index++) {
GayLun.skin = skins[index]; // 外部状态
GayLun.show()
}
```
#### 责任链模式
定义:将一系列处理方法连成链条,请求在这个链条中传递,直到遇到一个可以处理它的方法。
简介:如果大家接触过node开发,可能会比较好理解这个模式。node开发中我们会定义许多中间件依次挂载到node实例上。当服务器收到请求时请求回依次通过这些中间件方法,这些方法收到请求对象时会判断并处理该请求,然后传给下一个方法。
```
var ming = function(next, subject){
console.log(next);
if ( subject === '1+1' ){
console.log( '这题我会是2' )
} else {
next(subject) //我不知道下一个节点是谁,反正把请求往后面传递
}
};
var zhang = function(next, subject){
if ( subject === '1-1' ){
console.log( '这题我会是0' )
} else {
next(subject) //我不知道下一个节点是谁,反正把请求往后面传递
}
};
var wang = function(next, subject){
if ( subject === '1*1' ){
console.log( '这题我会是1' )
} else {
next(subject) //我不知道下一个节点是谁,反正把请求往后面传递
}
};
var Chain = function() {
this.line = []
this.index = 0
}
Chain.prototype.add = function(fn) {
this.line.push(fn)
}
Chain.prototype.exec = function() {
this.line[this.index](this.next.bind(this), ...arguments)
}
Chain.prototype.next = function() {
var fn = this.line[++ this.index]
if(fn) {
fn.apply(this, [this.next.bind(this), ...arguments])
} else {
console.log('end')
}
}
var studentChain = new Chain()
studentChain.add(ming)
studentChain.add(zhang)
studentChain.add(wang)
studentChain.exec('1+1')
```
#### 中介者模式
定义:解除对象与对象之间的紧耦合关系。增加一个中介者对象后,所有的 相关对象都通过中介者对象来通信。
简介:中介者使各对象之间耦合松散,而且可以独立地改变它们之间的交互。中介者
模式使网状的多对多关系变成了相对简单的一对多关系。中介者也被称为调停者,我们想象一下机场的指挥塔,如果没有指挥塔的存在,每一架飞机 要和方圆 100 公里内的所有飞机通信,才能确定航线以及飞行状况,后果是不可想象的。现实中 的情况是,每架飞机都只需要和指挥塔通信。指挥塔作为调停者,知道每一架飞机的飞行状况, 所以它可以安排所有飞机的起降时间,及时做出航线调整。
```
function Airplane(num) {
this.num = num
this.state = 'out'
}
function Airport(maxCount) {
this.planes = []
this.maxCount = maxCount
}
Airport.prototype.enter = function (plane) {
var count = this.planes.filter(e => e.state !== 'out').length
if(count < this.maxCount) {
var exist = this.planes.find(e => e.num === plane.num)
if(exist) {
exist.state = 'in'
} else {
plane.state = 'in'
this.planes.push(plane)
}
console.log('运行降落');
} else {
console.log('满了你飞别处去吧');
}
}
Airport.prototype.leave = function (plane) {
var exist = this.planes.find(e => e.num === plane.num)
exist.state = 'out'
}
var plane1 = new Airplane('1-1')
var plane2 = new Airplane('1-2')
var plane3 = new Airplane('1-3')
var airport = new Airport(2)
airport.enter(plane1)
airport.enter(plane2)
airport.enter(plane3)
airport.leave(plane2)
airport.enter(plane3)
```
#### 装饰者模式
定义:给对象动态地增加职责的方式称为装 饰者(decorator)模式
简介:装饰者模式能够在不改 变对象自身的基础上,在程序运行期间给对象 动态地添加职责。跟继承相比,装饰者是一种 更轻便灵活的做法(超类和子类之间存在强耦合性,当超类改变时,子类也会随之 改变)
例:在飞机大战游戏中,我们可以吃到一些额外的的武器包来强化我们的小飞机
```
var Plane = function () {
this.plane = plane;
}
Plane.prototype.fire = function () {
console.log('发射子弹');
}
var MissileDecorator = function (plane) {
this.plane = plane;
}
MissileDecorator.prototype.fire = function () {
this.plane.fire();
console.log('两侧发射导弹');
}
var plane = new Plane();
plane = new MissileDecorator(plane);
plane.fire();
```
#### 状态模式
定义:允许一个对象在其内部状态改变时改变它的行为,对象看起来似乎修改了它的类。
简介:状态模式的关键是区分事物内部的状态,事物内部状态的改变往往会带来事物的行为改变。
相对于策略模式来说状态模式中,状态 和状态对应的行为是早已被封装好的,状态之间的切换也早被规定完成,“改变行为”这件事情 发生在状态模式内部。对客户来说,并不需要了解这些细节。这正是状态模式的作用所在。
例:电灯开关
```
var OffLightState = function (light) {
this.light = light;
};
OffLightState.prototype.buttonWasPressed = function () {
console.log('开灯'); // offLightState 对应的行为
this.light.setState(this.light.onLightState); // 切换状态到onLightState
};
// WeakLightState:
var OnLightState = function (light) {
this.light = light;
};
OnLightState.prototype.buttonWasPressed = function () {
console.log('关灯'); // onLightState 对应的行为
this.light.setState(this.light.offLightState); // 切换状态到offLightState
};
var Light = function () {
this.offLightState = new OffLightState(this);
this.onLightState = new OnLightState(this);
this.currState = this.offLightState;
};
Light.prototype.buttonWasPressed = function () {
this.currState.buttonWasPressed();
};
Light.prototype.setState = function (newState) {
this.currState = newState;
};
var light = new Light();
light.buttonWasPressed() // 开灯
light.buttonWasPressed() // 关灯
```
#### 适配器模式
定义:通过包装函数,统一接口定义
简介:适配器模式主要用来解决两个已有接口之间不匹配的问题,它不考虑这些接口是怎样实 现的,也不考虑它们将来可能会如何演化。适配器模式不需要改变已有的接口,就能够 使它们协同作用。
例:
```
var googleMap = {
show: function(){
console.log( '开始渲染谷歌地图' );
}
};
var baiduMap = {
display: function(){
console.log( '开始渲染百度地图' );
}
};
var baiduMapAdapter = {
show: function(){
return baiduMap.display();
}
};
renderMap( googleMap ); // 输出:开始渲染谷歌地图
renderMap( baiduMapAdapter ); // 输出:开始渲染百度地图
```
## 总结
相信大家看完这些设计模式后心里不免会有疑惑,有些设计模式之间相似度非常高,但却被拆分为了不同的叫法(如:装饰者模式 和 代理模式,策略模式和状态模式等)。其实我觉得并不用纠结于此,设计模式本身就是为了优化代码性能,提高可读性、拓展性,更满足设计原则。只要能合理运用其中的一些代码技巧写出更好的代码,就行了。希望本系列的文章能对你的学习之路有帮助
### 系列链接
1. [16种JavaScript设计模式(上)](https://www.jianshu.com/p/455c0e34a3c0)
2. [16种JavaScript设计模式(中)](https://www.jianshu.com/p/993027963b60)
3. [16种JavaScript设计模式(下)](https://www.jianshu.com/p/0731e71475b2)
> 本文主要参考了《javascript设计模式与开发实践》一书