script,[object Object]

# 前端设计模式

设计模式(Design Pattern)是套被反复使、多数知晓的、经过分类的、代码设计经验的 总结。 任何事情都有套路,设计模式,就是写代码中的常⻅套路, 有些写法我们用常都⼀直在使用

# 订阅**/**发布模式 (观察者)

pub/sub 这个应该⼤家⽤到最⼴的设计模式了,

在这种模式中,并不是一个对象调用另一个对象的方法,而是一个对象订阅另⼀个对象的 特定活动并在

状态改编后获得通知。订阅者因此也成为观察者,而被观察的对象成为发布者或者主题。当发⽣了⼀个

重要事件时候 发布者会通知(调用)所有订阅者并且可能经常已事件对象的形式传递消息

  • 应用场景

    https://github.com/vuejs/vue/blob/dev/src/core/instance/events.js#L54

# 单例模式

单例模式的定义:保证一个类仅有一个实例,并提供一个访问它的全局访问点。实现的方法为先

判断实例存在与否,如果存在则直接返回,如果不存在就创建了再返回,这就确保了一个类只有

一个实例对象。

  • 适⽤场景

一个单一对象。比如:弹窗,无论点击多少次,弹窗只应该被创建一次' 实现起来也很简

单,用一个变量缓存即可

  • 应用场景

我们再element中的弹窗代码中,可以看到单例模式的实际案例 保证全局唯一性

https://github.com/ElemeFE/element/blob/dev/packages/message-box/src/main.js#L79

# 策略模式

策略模式的定义:定义一系列的算法,把他们一个个封装起来,并且使他们可以相互替换。

策略模式的作用的就是将算法的使⽤算法的实现分离开来。

一个基于策略模式的程序⾄少由两部分组成。第一个部分是一组策略类(可变),策略类封装了具体的

算法,并负责具体的计算过程。第二个部分是环境类Context(不变),Context接受客户的请求,随后

将请求委托给某一个策略类。要做到这一点,说明Context中要维持对某个策略对象的引用

举个栗子

奖金计算,绩效为 S 的人年 终奖有 4 倍工资,绩效为 A 的人年终奖有 3 倍工资,而绩效为 B 的人年终

奖是 2 倍工资

var calculateBonus = function( performanceLevel, salary ){ 

if ( performanceLevel === 'S' ){开课吧web全栈架构师 
return salary * 4; 
 } 

if ( performanceLevel === 'A' ){ 
return salary * 3; 
 } 

if ( performanceLevel === 'B' ){ 
return salary * 2; 
 } 
}; 

calculateBonus( 'B', 20000 ); // 输出:40000 

calculateBonus( 'S', 6000 ); // 输出:24000 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
  • 使用策略模式
var strategies = { 
"S": function( salary ){ 
return salary * 4; 
 }, 

"A": function( salary ){ 
return salary * 3; 
 }, 

"B": function( salary ){ 
return salary * 2; 
 } 
}; 

var calculateBonus = function( level, salary ){ 
return strategies[ level ]( salary ); 
}; 

console.log( calculateBonus( 'S', 20000 ) );// 输出:80000 

console.log( calculateBonus( 'A', 10000 ) );// 输出:30000 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  • 表单校验

正常写法

var registerForm = document.getElementById( 'registerForm' ); 

registerForm.onsubmit = function(){ 

if ( registerForm.userName.value === '' ){ 

alert ( '⽤户名不能为空' ); 

return false; 

 }开课吧web全栈架构师 

if ( registerForm.password.value.length < 6 ){ 

alert ( '密码⻓度不能少于 6 位' ); 

return false; 

 } 

if ( !/(^1[3|5|8][0-9]{9}$)/.test( registerForm.phoneNumber.value ) ){ 

alert ( '⼿机号码格式不正确' ); 

return false; 

 } 

} 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
  • 使用策略模式
var strategies = { 

isNonEmpty: function( value, errorMsg ){ 

if ( value === '' ){ 

return errorMsg ; 

 } 

 }, 

minLength: function( value, length, errorMsg ){ 

if ( value.length < length ){ 

return errorMsg; 

 } 

 }, 

isMobile: function( value, errorMsg ){ // ⼿机号码格式 

if ( !/(^1[3|5|8][0-9]{9}$)/.test( value ) ){ 

return errorMsg; 

 } 

 } 

}; 

var Validator = function(){ 

this.cache = []; // 保存校验规则 

}; 

Validator.prototype.add = function( 

var ary = rule.split( ':' ); 

this.cache.push(function(){ // 

var strategy = ary.shift(); 

ary.unshift( dom.value ); 

ary.push( errorMsg ); // 

return strategies[strategy].apply(dom, ary); 

 }); 

}; 

Validator.prototype.start = function(){ 

for ( var i = 0, validatorFunc; validatorFunc = this.cache[ i++ ]; ){ 

var msg = validatorFunc(); // 开始校验,并取得校验后的返回信息 

if ( msg ){ // 如果有确切的返回值,说明校验没有通过 

return msg; 

 } 

 }

}; 

var validataFunc = function(){ 

var validator = new Validator(); // 创建⼀个 validator 对象 

/***************添加⼀些校验规则****************/ 

validator.add( registerForm.userName, 'isNonEmpty', '⽤户名不能为空' );  

validator.add( registerForm.password, 'minLength:6', '密码⻓度不能少于 6位'); 

validator.add( registerForm.phoneNumber, 'isMobile', '⼿机号码格式不正确' ); 

var errorMsg = validator.start(); // 获得校验结果 

return errorMsg; // 返回校验结果 

} 

var registerForm = document.getElementById( 'registerForm' ); 

registerForm.onsubmit = function(){ 

var errorMsg = validataFunc(); // 如果 errorMsg 有确切的返回值,说明未通过校验 

if ( errorMsg ){ 

alert ( errorMsg ); 

return false; // 阻⽌表单提交 

 } 

}; 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107

# 代理模式

代理模式的定义:为⼀个对象提供⼀个代⽤品或占位符,以便控制对它的访问。

常⽤的虚拟代理形式:某一个花销很大的操作,可以通过虚拟代理的方式延迟到这种需要它的时候才去

创建(例:使用虚拟代理实现图片懒加载)

图片懒加载的方式:先通过一张loading图占位,然后通过异步的方式加载图片,等图片加载好了再把

完成的图片加载到img标签当中。

假设我们在做⼀个⽂件同步的功能,当我们选中⼀个 checkbox 的时候,它对应的⽂件就会被同 步到另

外⼀台备⽤服务器上⾯。当⼀次选中过多时,会产⽣频繁的⽹络请求。将带来很⼤的开销。可以通过⼀

个代理函数 proxySynchronousFile 来收集⼀段时间之内的请求, 最后⼀次性发送给服务器

# 中介者模式

中介者模式的定义:通过⼀个中介者对象,其他所有的相关对象都通过该中介者对象来通信,⽽不是相

互引⽤,当其中的⼀个对象发⽣改变时,只需要通知中介者对象即可。通过中介者模式可以解除对象与

对象之间的紧耦合关系。

例如:现实⽣活中,航线上的⻜机只需要和机场的塔台通信就能确定航线和⻜⾏状态,⽽不需要和所有

⻜机通信。同时塔台作为中介者,知道每架⻜机的⻜⾏状态,所以可以安排所有⻜机的起降和航线安

排。

中介者模式适⽤的场景:例如购物⻋需求,存在商品选择表单、颜⾊选择表单、购买数量表单等等,都

会触发change事件,那么可以通过中介者来转发处理这些事件,实现各个事件间的解耦,仅仅维护中介

者对象即可。

redux,vuex 都属于中介者模式的实际应⽤,我们把共享的数据,抽离成⼀个单独的store, 每个都通

过store这个中介来操作对象

⽬的就是减少耦合

# 装饰器模式

装饰者模式的定义:在不改变对象⾃身的基础上,在程序运⾏期间给对象动态地添加⽅法。常⻅应⽤,

react的⾼阶组件, 或者react-redux中的@connect 或者⾃⼰定义⼀些⾼阶组件

import React from 'react' 

const withLog = Component=>{ 

// 类组件 

class NewComponent extends React.Component{ 

componentWillMount(){ 

console.time(`CompoentRender`) 

console.log(`准备完毕了`) 

 } 

render(){ 

return <Component {...this.props}></Component> 

 } 

componentDidMount(){ 

console.timeEnd(`CompoentRender`) 

console.log(`渲染完毕了`) 

 } 

 } 

return NewComponent 

} 

export {withLog} 

@withLog 

class XX 

export const connect = (mapStateToProps = state => state, mapDispatchToProps = 

{}) => (WrapComponent) => { 

return class ConnectComponent extends React.Component { 

static contextTypes = { 

store: PropTypes.object开课吧web全栈架构师 

} 

constructor(props, context) { 

super(props, context) 

this.state = { 

props: {} 

} 

} 

componentDidMount() { 

const { store } = this.context 

// 当前状态 update 后, 放⼊监听器中, ⽤于下⼀次的更新(每次 dispatch 后会执⾏ 

subscribe 中的所有函数) 

store.subscribe(() => this.update()) 

this.update() 

} 

update() { 

const { store } = this.context 

const stateProps = mapStateToProps(store.getState()) 

const dispatchProps = bindActionCreators(mapDispatchToProps, 

store.dispatch) 

this.setState({ 

props: { 

...this.state.props, 

...stateProps, 

...dispatchProps 

} 

}) 

} 

render() { 

return <WrapComponent {...this.state.props}></WrapComponent> 

} 

} 

} 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115

假设我们在编写⼀个⻜机⼤战的游戏,随着经验值的增加,我们操作的⻜机对象可以升级成更厉害的⻜

机,⼀开始这些⻜机只能发射普通的⼦弹,升到第⼆级时可以发射导弹,升到第三级时可以发射原⼦

弹。

Function.prototype.before = function( beforefn ){

var __self = this; // 保存原函数的引⽤

return function(){ // 返回包含了原函数和新函数的"代理"函数

beforefn.apply( this, arguments ); // 执⾏新函数,且保证 this 不被劫持,新函

数接受的参数 // 也会被原封不动地传⼊原函数,新函数在原函数之前执⾏

return __self.apply( this, arguments ); // 执⾏原函数并返回原函数的执⾏结

果, // 并且保证 this 不被劫持开课吧web全栈架构师

} }

Function.prototype.after = function( afterfn ){

var __self = this;

return function(){

var ret = __self.apply( this, arguments );

afterfn.apply( this, arguments );

return ret;

}

};

⽐如⻚⾯中有⼀个登录 button,点击这个 button 会弹出登录浮层,与此同时要进⾏数据上报, 来统计

有多少⽤户点击了这个登录 button

var showLogin = function(){

console.log( '打开登录浮层' );

log( this.getAttribute( 'tag' ) );

}

var log = function( tag ){

console.log( '上报标签为: ' + tag );

(new Image).src = 'http:// xxx.com/report?tag=' + tag;

}

document.getElementById( 'button' ).onclick = showLogin;

使⽤装饰器

var showLogin = function(){

console.log( '打开登录浮层' );

}

var log = function(){

console.log( '上报标签为: ' + this.getAttribute( 'tag' ) );

}

showLogin = showLogin.after( log ); // 打开登录浮层之后上报数据

document.getElementById( 'button' ).onclick = showLogin;

装饰者模式和代理模式的结构看起来⾮常相像,这两种模式都描述了怎样为对象提供 ⼀定程度上的间接

引⽤,它们的实现部分都保留了对另外⼀个对象的引⽤,并且向那个对象发送 请求。 代理模式和装饰

者模式最重要的区别在于它们的意图和设计⽬的。代理模式的⽬的是,当直接访问本体不⽅便或者不符

合需要时,为这个本体提供⼀个替代者。本体定义了关键功能,⽽代理提供或拒绝对它的访问,或者在

访问本体之前做⼀些额外的事情。装饰者模式的作⽤就是为对 象动态加⼊⾏为。开课吧web全栈架构师

其实Vue中的v-input,v-checkbox也可以认为是装饰器模式, 对原⽣的input和checkbox做⼀层装饰

# 外观模式

外观模式即让多个⽅法⼀起被调⽤

涉及到兼容性,参数⽀持多格式,有很多这种代码,对外暴露统⼀的api,⽐如上⾯的$on ⽀持数组,

¥offff参数⽀持多个情况, 对⾯只⽤⼀个函数,内部判断实现

⾃⼰封装组件库 经常看到

myEvent = {

stop: function(e) {

if (typeof e.preventDefault() === "function") {

e.preventDefault();

}

if (typeof e.stopPropagation() === "function") {

e.stopPropagation();

}

//for IE

if (typeof e.returnValue === "boolean") {

e.returnValue = false;

}

if (typeof e.cancelBubble === "boolean") {

e.cancelBubble = true;

}

}

addEvent(dom, type, fn) {

if (dom.addEventListener) {

dom.addEventListener(type, fn, false);

} else if (dom.attachEvent) {

dom.attachEvent('on' + type, fn);

} else {

dom['on' + type] = fn;

}

}

}

# 工厂模式

提供创建对象的接⼝,把成员对象的创建⼯作转交给⼀个外部对象,好处在于消除对象之间的耦合(也就

是相互影响)开课吧web全栈架构师

常⻅的例⼦,我们的弹窗,message,对外提供的api,都是调⽤api,然后新建⼀个弹窗或者Message

的实例,就是典型的⼯⼚模式

const Notification = function(options) {

if (Vue.prototype.$isServer) return;

options = options || {};

const userOnClose = options.onClose;

const id = 'notification_' + seed++;

const position = options.position || 'top-right';

options.onClose = function() {

Notification.close(id, userOnClose);

};

instance = new NotificationConstructor({

data: options

});

if (isVNode(options.message)) {

instance.$slots.default = [options.message];

options.message = 'REPLACED_BY_VNODE';

}

instance.id = id;

instance.$mount();

document.body.appendChild(instance.$el);

instance.visible = true;

instance.dom = instance.$el;

instance.dom.style.zIndex = PopupManager.nextZIndex();

let verticalOffset = options.offset || 0;

instances.filter(item => item.position === position).forEach(item => {

verticalOffset += item.$el.offsetHeight + 16;

});

verticalOffset += 16;

instance.verticalOffset = verticalOffset;

instances.push(instance);

return instance;

};

https://github.com/ElemeFE/element/blob/dev/packages/notifification/src/main.js#L11

# 建造者模式

和⼯⻓模式相⽐,参与了更多创建的过程 或者更复杂开课吧web全栈架构师

var Person = function(name, work) {

// 创建应聘者缓存对象

var _person = new Human();

// 创建应聘者姓名解析对象

_person.name = new Named(name);

// 创建应聘者期望职位

_person.work = new Work(work);

return _person;

};

var person = new Person('xiao ming', 'code');

console.log(person)

# 迭代器模式

迭代器模式是指提供⼀种⽅法顺序访问⼀个聚合对象中的各个元素,⽽⼜不需要暴露该对象的内

部表示。迭代器模式可以把迭代的过程从业务逻辑中分离出来,在使⽤迭代器模式之后,即使不关

⼼对象的内部构造,也可以按顺序访问其中的每个元素

这个⽤的就太多了 each map啥乱遭的

var each = function( ary, callback ){

for ( var i = 0, l = ary.length; i < l; i++ ){

callback.call( ary[i], i, ary[ i ] );

}

};

each( [ 1, 2, 3 ], function( i, n ){

alert ( [ i, n ] );

})

# 享元模式

享元(flflyweight)模式是⼀种⽤于性能优化的模式,“flfly”在这⾥是苍蝇的意思,意为蝇量级。享元模

式的核⼼是运⽤共享技术来有效⽀持⼤量细粒度的对象。 如果系统中因为创建了⼤量类似的对象

⽽导致内存占⽤过⾼,享元模式就⾮常有⽤了。在 JavaScript 中,浏览器特别是移动端的浏览器

分配的内存并不算多,如何节省内存就成了⼀件⾮常有意义的事情。

假设有个内⾐⼯⼚,⽬前的产品有 50 种男式内⾐和 50 种⼥⼠内⾐,为了推销产品,⼯⼚决定⽣产⼀些

塑料模特来穿上他们的内⾐拍成⼴告照⽚。 正常情况下需要 50个男模特和50个⼥模特,然后让他们每

⼈分别穿上⼀件内⾐来拍照。开课吧web全栈架构师

var Model = function( sex, underwear){

this.sex = sex;

this.underwear = underwear;

};

Model.prototype.takePhoto = function(){

console.log( 'sex= ' + this.sex + ' underwear=' + this.underwear);

};

for ( var i = 1; i <= 50; i++ ){

var maleModel = new Model( 'male', 'underwear' + i );

maleModel.takePhoto();

};

for ( var j = 1; j <= 50; j++ ){

var femaleModel= new Model( 'female', 'underwear' + j );

femaleModel.takePhoto();

};

如上所述,现在⼀共有 50 种男内 ⾐和 50 种⼥内⾐,所以⼀共会产⽣ 100 个对象。如果将来⽣产了

10000 种内⾐,那这个程序可能会因为存在如此多的对象已经提前崩溃。 下⾯我们来考虑⼀下如何优化

这个场景。虽然有 100 种内⾐,但很显然并不需要 50 个男 模特和 50 个⼥模特。其实男模特和⼥模特

各⾃有⼀个就⾜够了,他们可以分别穿上不同的内⾐来拍照。

/*只需要区别男⼥模特

那我们先把 underwear 参数从构造函数中 移除,构造函数只接收 sex 参数*/

var Model = function( sex ){

this.sex = sex;

};

Model.prototype.takePhoto = function(){

console.log( 'sex= ' + this.sex + ' underwear=' + this.underwear);

};

/分别创建⼀个男模特对象和⼀个⼥模特对象/

var maleModel = new Model( 'male' ),

femaleModel = new Model( 'female' );

/给男模特依次穿上所有的男装,并进⾏拍照/

for ( var i = 1; i <= 50; i++ ){

maleModel.underwear = 'underwear' + i;

maleModel.takePhoto();

};

/给⼥模特依次穿上所有的⼥装,并进⾏拍照/

for ( var j = 1; j <= 50; j++ ){

femaleModel.underwear = 'underwear' + j;

femaleModel.takePhoto();

};

//只需要两个对象便完成了同样的功能

内部状态存储于对象内部

内部状态可以被⼀些对象共享开课吧web全栈架构师

内部状态独⽴于具体的场景,通常不会改变

外部状态取决于具体的场景,并根据场景⽽变化,外部状态不能被共享

性别是内部状态,内⾐是外部状态,通过区分这两种状态,⼤⼤减少了系 统中的对象数量。通常来讲,

内部状态有多少种组合,系统中便最多存在多少个对象,因为性别 通常只有男⼥两种,所以该内⾐⼚商

最多只需要 2 个对象。

应⽤案例

消息组件开课吧web全栈架构师

需求

\1. 弹窗逻辑⼀样

\2. 四中弹窗,颜⾊,icon不同

\3. 接收⽂案

交互⽅式——弹出、隐藏,由共享对象所拥有

提示icon、背景样式、字体样式提供接⼝可配置

使⽤api统⼀

Dialog只会在项⽬初始化时被 new ⼀次,每次使⽤Message组件通过改变Dialog的状态获取组件

DOM,其实很容易知道new⼀个组件的成本要⽐⼀个组件的更新成本⾼很多

# 职责链模式

职责链模式的定义是:使多个对象都有机会处理请求,从⽽避免请求的发送者和接收者之间的耦合关系,

将这些对象连成⼀条链,并沿着这条链传递该请求,直到有⼀个对象处理它为⽌。 职责链模式的名字⾮

常形象,⼀系列可能会处理请求的对象被连接成⼀条链,请求在这些对 象之间依次传递,直到遇到⼀个

可以处理它的对象,我们把这些对象称为链中的节点

假设我们负责⼀个售卖⼿机的电商⽹站,经过分别交纳 500 元定⾦和 200 元定⾦的两轮预定后(订单已

在此时⽣成),现在已经到了正式购买的阶段。 公司针对⽀付过定⾦的⽤户有⼀定的优惠政策。在正式

购买后,已经⽀付过 500 元定⾦的⽤ 户会收到 100 元的商城优惠券,200 元定⾦的⽤户可以收到 50 元

的优惠券,⽽之前没有⽀付定⾦的⽤户只能进⼊普通购买模式,也就是没有优惠券,且在库存有限的情

况下不⼀定保证能买到。

var order = function( orderType, pay, stock ){

if ( orderType === 1 ){ // 500 元定⾦购买模式

if ( pay === true ){ // 已⽀付定⾦

console.log( '500 元定⾦预购, 得到 100 优惠券' );

} else{ // 未⽀付定⾦,降级到普通购买模式

if ( stock > 0 ){ // ⽤于普通购买的⼿机还有库存

console.log( '普通购买, ⽆优惠券' );

}else{

console.log( '⼿机库存不⾜' );

}

}

} else if ( orderType === 2 ){

if ( pay === true ){ // 200 元定⾦购买模式

console.log( '200 元定⾦预购, 得到 50 优惠券' );

}else{

if ( stock > 0 ){

console.log( '普通购买, ⽆优惠券' );

}else{

console.log( '⼿机库存不⾜' );

}开课吧web全栈架构师

}

} else if (orderType === 3) {

if ( stock > 0 ){

console.log( '普通购买, ⽆优惠券' );

} else{

console.log( '⼿机库存不⾜' );

}

}

};

order( 1 , true, 500); // 输出: 500 元定⾦预购, 得到 100 优惠券

现在我们采⽤职责链模式重构这段代码,先把 500 元订单、200 元订单以及普通购买分成 3 个函数。

接下来把 orderType、pay、stock 这 3 个字段当作参数传递给 500 元订单函数,如果该函数不符合处

理条件,则把这个请求传递给后⾯的 200 元订单函数,如果 200 元订单函数依然不能处理该请求,则

继续传递请求给普通购买函数。

var order500 = function( orderType, pay, stock ){

if ( orderType === 1 && pay === true ){

console.log( '500 元定⾦预购,得到 100 优惠券' );

} else{

return 'nextSuccessor'; // 我不知道下⼀个节点是谁,反正把请求往后⾯传递

}

};

var order200 = function( orderType, pay, stock ){

if ( orderType === 2 && pay === true ){

console.log( '200 元定⾦预购,得到 50 优惠券' );

} else{

return 'nextSuccessor'; // 我不知道下⼀个节点是谁,反正把请求往后⾯传递

}

};

var orderNormal = function( orderType, pay, stock ){

if ( stock > 0 ){

console.log( '普通购买,⽆优惠券' );

} else{

console.log( '⼿机库存不⾜' );

}

};

// Chain.prototype.setNextSuccessor 指定在链中的下⼀个节点

// Chain.prototype.passRequest 传递请求给某个节点

var Chain = function( fn ){

this.fn = fn;

this.successor = null;

};

Chain.prototype.setNextSuccessor = function( successor ){

return this.successor = successor;开课吧web全栈架构师

};

Chain.prototype.passRequest = function(){

var ret = this.fn.apply( this, arguments );

if ( ret === 'nextSuccessor' ){

return this.successor && this.successor.passRequest.apply(

this.successor, arguments );

}

return ret;

};

var chainOrder500 = new Chain( order500 );

var chainOrder200 = new Chain( order200 );

var chainOrderNormal = new Chain( orderNormal );

chainOrder500.setNextSuccessor( chainOrder200 );

chainOrder200.setNextSuccessor( chainOrderNormal);

chainOrder500.passRequest( 1, true, 500 ); // 输出:500 元定⾦预购,得到 100 优惠

chainOrder500.passRequest( 2, true, 500 ); // 输出:200 元定⾦预购,得到 50 优惠

chainOrder500.passRequest( 3, true, 500 ); // 输出:普通购买,⽆优惠券

chainOrder500.passRequest( 1, false, 0 ); // 输出:⼿机库存不⾜

通过改进,我们可以⾃由灵活地增加、移除和修改链中的节点顺序,假如某天⽹站运营⼈员 ⼜想出了⽀

持 300 元定⾦购买,那我们就在该链中增加⼀个节点即可

var order300 = function(){

// 具体实现略

};

chainOrder300= new Chain( order300 );

chainOrder500.setNextSuccessor( chainOrder300);

chainOrder300.setNextSuccessor( chainOrder200);

# 适配器模式

适配器模式的作⽤是解决两个软件实体间的接⼝不兼容的问题。使⽤适配器模式之后,原本 由于

接⼝不兼容⽽不能⼯作的两个软件实体可以⼀起⼯作。 适配器的别名是包装器(wrapper),这是⼀

个相对简单的模式。在程序开发中有许多这样的 场景:当我们试图调⽤模块或者对象的某个接⼝

时,却发现这个接⼝的格式并不符合⽬前的需求。 这时候有两种解决办法,第⼀种是修改原来的

接⼝实现,但如果原来的模块很复杂,或者我们拿 到的模块是⼀段别⼈编写的经过压缩的代码,

修改原接⼝就显得不太现实了。第⼆种办法是创建 ⼀个适配器,将原接⼝转换为客户希望的另⼀

个接⼝,客户只需要和适配器打交道。开课吧web全栈架构师

var googleMap = {

show: function(){

console.log( '开始渲染⾕歌地图' );

}

};

var baiduMap = {

display: function(){

console.log( '开始渲染百度地图' );

}

};

var baiduMapAdapter = {

show: function(){

return baiduMap.display();

}

};

renderMap( googleMap ); // 输出:开始渲染⾕歌地图

renderMap( baiduMapAdapter ); // 输出:开始渲染百度地图

适配器模式主要⽤来解决两个已有接⼝之间不匹配的问题,它不考虑这些接⼝是怎样实 现的,也不考虑

它们将来可能会如何演化。适配器模式不需要改变已有的接⼝,就能够 使它们协同作⽤。

装饰者模式和代理模式也不会改变原有对象的接⼝,但装饰者模式的作⽤是为了给对象 增加功能。装饰

者模式常常形成⼀条⻓的装饰链,⽽适配器模式通常只包装⼀次。代理 模式是为了控制对对象的访问,

通常也只包装⼀次。

我们设计很多插件,有默认值,也算是适配器的⼀种应⽤, vue的prop校验,default也算是适配器的

应⽤了

外观模式的作⽤倒是和适配器⽐较相似,有⼈把外观模式看成⼀组对象的适配器,但外观模式最显著的

特点是定义了⼀个新的接⼝。

# 模板方法模式

模板⽅法模式在⼀个⽅法中定义⼀个算法的⻣架,⽽将⼀些步骤的实现延迟到⼦类中。模板⽅法

使得⼦类可以在不改变算法结构的情况下,重新定义算法中某些步骤的具体实现

这个我们⽤的很多,vue中的slot,react中的children

# 备忘录模式

可以恢复到对象之前的某个状态,其实⼤家学习react或者redux的时候,时间旅⾏的功能,就算是备忘

录模式的⼀个应⽤

https://zh-hans.reactjs.org/tutorial/tutorial.html#implementing-time-travel开课吧web全栈架构师

关于评论

评论前请填好“昵称”、“邮箱”这两栏内容,否则不会收到回复,谢谢!

  • +
  • -