## 簡介
緊接[上文(地址)](http://www.itdecent.cn/p/993027963b60),我們繼續(xù)介紹以下幾種設(shè)計模式
* 模板方法模式
* 享元模式
* 責任鏈模式
* 中介者模式
* 裝飾者模式
* 狀態(tài)模式
* 適配器模式
#### 模板方法模式
定義:基于繼承的設(shè)計模式,在父類中定義需要實現(xiàn)的方法,并定義好方法的執(zhí)行邏輯,子類只需實現(xiàn)對應(yīng)方法即可
簡介:模板方法模式是一種只需通過繼承就可以實現(xiàn)的簡單模式。這種模式在前端框架中十分常見,vue、react的組件定義都用到了該模式。大家思考下為什么我們在vue組件中定義的生命周期函數(shù)會按照給定的順序執(zhí)行呢?通過下面的簡單示例來了解下。
例:
```
var Vue = function (config) { // 父類
? ? this.config = config
}
// 定義需要實現(xiàn)的方法
Vue.prototype.created = function () {}
Vue.prototype.mounted = function () {}
Vue.prototype.destoryed = function () {}
// 定義執(zhí)行邏輯
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()
```
#### 享元模式
定義:將對象屬性劃分為內(nèi)、外兩種屬性,共享內(nèi)部狀態(tài)節(jié)約內(nèi)存
簡介:享元模式是一種用于性能優(yōu)化的模式,避免創(chuàng)建大量類似的對象,占用大量內(nèi)存。
例:
```
var Hero = function (name) {
? ? this.name = name // 內(nèi)部狀態(tài)
}
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]; // 外部狀態(tài)
? ? GayLun.show()
}
```
#### 責任鏈模式
定義:將一系列處理方法連成鏈條,請求在這個鏈條中傳遞,直到遇到一個可以處理它的方法。
簡介:如果大家接觸過node開發(fā),可能會比較好理解這個模式。node開發(fā)中我們會定義許多中間件依次掛載到node實例上。當服務(wù)器收到請求時請求回依次通過這些中間件方法,這些方法收到請求對象時會判斷并處理該請求,然后傳給下一個方法。
```
var ming = function(next, subject){
? ? console.log(next);
? ? if ( subject === '1+1' ){
? ? ? ? console.log( '這題我會是2' )
? ? } else {
? ? ? ? next(subject) //我不知道下一個節(jié)點是誰,反正把請求往后面?zhèn)鬟f
? ? }
};
var zhang = function(next, subject){
? ? if ( subject === '1-1' ){
? ? ? ? console.log( '這題我會是0' )
? ? } else {
? ? ? ? next(subject) //我不知道下一個節(jié)點是誰,反正把請求往后面?zhèn)鬟f
? ? }
};
var wang = function(next, subject){
? ? if ( subject === '1*1' ){
? ? ? ? console.log( '這題我會是1' )
? ? } else {
? ? ? ? next(subject) //我不知道下一個節(jié)點是誰,反正把請求往后面?zhèn)鬟f
? ? }
};
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')
```
#### 中介者模式
定義:解除對象與對象之間的緊耦合關(guān)系。增加一個中介者對象后,所有的 相關(guān)對象都通過中介者對象來通信。
簡介:中介者使各對象之間耦合松散,而且可以獨立地改變它們之間的交互。中介者
模式使網(wǎng)狀的多對多關(guān)系變成了相對簡單的一對多關(guān)系。中介者也被稱為調(diào)停者,我們想象一下機場的指揮塔,如果沒有指揮塔的存在,每一架飛機 要和方圓 100 公里內(nèi)的所有飛機通信,才能確定航線以及飛行狀況,后果是不可想象的?,F(xiàn)實中 的情況是,每架飛機都只需要和指揮塔通信。指揮塔作為調(diào)停者,知道每一架飛機的飛行狀況, 所以它可以安排所有飛機的起降時間,及時做出航線調(diào)整。
```
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)
```
#### 裝飾者模式
定義:給對象動態(tài)地增加職責的方式稱為裝 飾者(decorator)模式
簡介:裝飾者模式能夠在不改 變對象自身的基礎(chǔ)上,在程序運行期間給對象 動態(tài)地添加職責。跟繼承相比,裝飾者是一種 更輕便靈活的做法(超類和子類之間存在強耦合性,當超類改變時,子類也會隨之 改變)
例:在飛機大戰(zhàn)游戲中,我們可以吃到一些額外的的武器包來強化我們的小飛機
```
var Plane = function () {
? ? this.plane = plane;
}
Plane.prototype.fire = function () {
? ? console.log('發(fā)射子彈');
}
var MissileDecorator = function (plane) {
? ? this.plane = plane;
}
MissileDecorator.prototype.fire = function () {
? ? this.plane.fire();
? ? console.log('兩側(cè)發(fā)射導彈');
}
var plane = new Plane();
plane = new MissileDecorator(plane);
plane.fire();
```
#### 狀態(tài)模式
定義:允許一個對象在其內(nèi)部狀態(tài)改變時改變它的行為,對象看起來似乎修改了它的類。
簡介:狀態(tài)模式的關(guān)鍵是區(qū)分事物內(nèi)部的狀態(tài),事物內(nèi)部狀態(tài)的改變往往會帶來事物的行為改變。
相對于策略模式來說狀態(tài)模式中,狀態(tài) 和狀態(tài)對應(yīng)的行為是早已被封裝好的,狀態(tài)之間的切換也早被規(guī)定完成,“改變行為”這件事情 發(fā)生在狀態(tài)模式內(nèi)部。對客戶來說,并不需要了解這些細節(jié)。這正是狀態(tài)模式的作用所在。
例:電燈開關(guān)
```
var OffLightState = function (light) {
? ? this.light = light;
};
OffLightState.prototype.buttonWasPressed = function () {
? ? console.log('開燈'); // offLightState 對應(yīng)的行為
? ? this.light.setState(this.light.onLightState); // 切換狀態(tài)到onLightState
};
// WeakLightState:
var OnLightState = function (light) {
? ? this.light = light;
};
OnLightState.prototype.buttonWasPressed = function () {
? ? console.log('關(guān)燈'); // onLightState 對應(yīng)的行為
? ? this.light.setState(this.light.offLightState); // 切換狀態(tài)到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() // 關(guān)燈
```
#### 適配器模式
定義:通過包裝函數(shù),統(tǒng)一接口定義
簡介:適配器模式主要用來解決兩個已有接口之間不匹配的問題,它不考慮這些接口是怎樣實 現(xiàn)的,也不考慮它們將來可能會如何演化。適配器模式不需要改變已有的接口,就能夠 使它們協(xié)同作用。
例:
```
var googleMap = {
show: function(){
console.log( '開始渲染谷歌地圖' );
}
};
var baiduMap = {
display: function(){
console.log( '開始渲染百度地圖' );
}
};
var baiduMapAdapter = {
show: function(){
return baiduMap.display();
}
};
renderMap( googleMap ); // 輸出:開始渲染谷歌地圖
renderMap( baiduMapAdapter ); // 輸出:開始渲染百度地圖
```
## 總結(jié)
相信大家看完這些設(shè)計模式后心里不免會有疑惑,有些設(shè)計模式之間相似度非常高,但卻被拆分為了不同的叫法(如:裝飾者模式 和 代理模式,策略模式和狀態(tài)模式等)。其實我覺得并不用糾結(jié)于此,設(shè)計模式本身就是為了優(yōu)化代碼性能,提高可讀性、拓展性,更滿足設(shè)計原則。只要能合理運用其中的一些代碼技巧寫出更好的代碼,就行了。希望本系列的文章能對你的學習之路有幫助
### 系列鏈接
1. [16種JavaScript設(shè)計模式(上)](http://www.itdecent.cn/p/455c0e34a3c0)
2. [16種JavaScript設(shè)計模式(中)](http://www.itdecent.cn/p/993027963b60)
3. [16種JavaScript設(shè)計模式(下)](http://www.itdecent.cn/p/0731e71475b2)
> 本文主要參考了《javascript設(shè)計模式與開發(fā)實踐》一書