做者:gauseenjavascript
Singleton Pattern
)描述:html
只對外暴露一個對象前端
// 只建立一個實例 const mySingleton = (function () { let instance = null // 初始化方法 let init = () => { console.log('init logic') return { someThing () { alert('hello world') }, } } return { getInstance () { // 不存在 instance 建立新的,已存在則直接返回(只建立一次) if (!instance) { instance = init() } return instance }, } })()
// 每次都建立一個新的實例 const myBadSingleton = (function () { let instance = null // 初始化方法 let init = () => { console.log('init logic') return { someThing () { alert('hello world') }, } } return { getInstance () { // 每次都建立一個新的實例(建立屢次) instance = init() return instance }, } })()
Observer Pattern
)真實場景:java
以公衆號爲例,有些人訂閱(關注)了某公衆號,當公衆號發佈新的消息時,訂閱者就會收到該消息的推送git
描述:github
定義對象之間的依賴關係,每當對象更改狀態時,都會通知全部依賴項瀏覽器
// 觀察者 class Observer { constructor (name) { this.name = name } // 每一個觀察者都有一個 smile 事件 update () { console.log(`${this.name} is updated`) } } // 主體 class Subject { constructor () { // 觀察者列表 this.observers = [] } // 添加觀察者到主體的觀察者列表 subscribe (observer) { this.observers.push(observer) } // 通知全部觀察者 notify () { this.observers.forEach(observerItem => { observerItem.update() }) } } // 建立主體實例 const subject = new Subject() // 有 2 個觀察者 observer0一、observer02 const observer01 = new Observer('observer_01') const observer02 = new Observer('observer_02') // 觀察者訂閱主體 subject.subscribe(observer01) subject.subscribe(observer02) // 通知全部觀察者 subject.notify()
Publish/Subscribe Pattern
)真實場景:post
報社將報紙發送給郵局,郵局按照訂閱關係將報紙發送給對應的人this
描述:spa
消息的發送者(稱爲發佈者)不會將消息直接發送給特定的接收者(稱爲訂閱者)。而是將發佈的消息分爲不一樣的類別,無需瞭解哪些訂閱者。一樣的,訂閱者能夠訂閱一個或多個類別,只接收感興趣的消息,無需瞭解哪些發佈者
// 訂閱者 class Subscriber { constructor (name) { this.name = name } // 每一個觀察者都有一個 smile 事件 update () { console.log(`${this.name} is updated`) } } // 調度中心 class EventHub { constructor () { // 訂閱主題 this.topics = {} } // 訂閱 subscribe (topicType, subscriber) { if (!this.topics[topicType]) { this.topics[topicType] = [] } this.topics[topicType].push(subscriber) } // 給對應主題下的訂閱者發佈內容 publish (topicType) { let topicList = this.topics[topicType] if (!topicList) return topicList.forEach(subscriberItem => { subscriberItem.update() }) } } // 建立一個「調度中心」 let eventHub = new EventHub() // 建立 3 個訂閱者 subscriber0一、subscriber0二、subscriber03 let subscriber01 = new Subscriber('subscriber_01') let subscriber02 = new Subscriber('subscriber_02') let subscriber03 = new Subscriber('subscriber_03') // 訂閱者註冊「調度中心」 eventHub.subscribe('typeA', subscriber01) eventHub.subscribe('typeA', subscriber02) eventHub.subscribe('typeB', subscriber03) // 發佈者對 typeA 主題發佈內容 eventHub.publish('typeA') // subscriber_01 is updated // subscriber_02 is updated // 發佈者對 typeB 主題發佈內容 eventHub.publish('typeA') // subscriber_03 is updated
如上圖
Subject
)和觀察者(Observer
)是強耦合,有直接的聯繫,相互知道對方的存在。Subject
)和觀察者(Observer
)是鬆耦合,沒有直接的聯繫,是經過「事件通道」(調度中心)創建聯繫,互相不知道對方存在。奇葩解釋:觀察者模式,沒中間商賺差價;發佈-訂閱模式,有中間商賺差價
Factory Pattern
)class CarA { constructor (model, color) { console.log('CarA') } } class CarB { constructor (model, color) { console.log('CarB') } } class CarFactory { carA () { return CarA } carB () { return CarB } create (type) { let CarClass = this[type]() return new CarClass() } } let carFactory = new CarFactory() // 經過 carFactory 建立不一樣的 car let carA = carFactory.create('carA') let carB = carFactory.create('carB')
Adapter Pattern
)真實場景:
如今好多智能手機都去掉了 3.5mm
的耳機孔,取而代之的是 Type-C
接口耳機。可是你還想在新手機中使用 3.5mm
的老式耳機怎麼辦?
沒錯,那就須要用適配器,Type-C
轉 3.5mm
的適配器。以下圖 :
描述:
能夠將其餘不兼容的對象包裝在適配器中,使它與另外一個類兼容
// Type-C 耳機 class TypecHeadset { typeC () { console.log('Type-C 耳機已插入') } } // 3.5 毫米耳機 class Dot35mmHeadset { dot35mm () { console.log('3.5mm 耳機已插入') } } // 適配器(3.5 毫米 --> Type-C) class Dot35mmToTypecAdapter { constructor (dot35mmHeadset) { this.dot35mmHeadset = dot35mmHeadset } typeC () { this.dot35mmHeadset.dot35mm() } } // 手機類(該手機只支持 Type-C 耳機) class Phone { // 插入耳機孔 insertTypeC (headset) { headset.typeC() } } let phone = new Phone() // Type-C 耳機 let typecHeadset = new TypecHeadset() // 3.5 毫米耳機 let dot35mmHeadset = new Dot35mmHeadset() // 耳機適配器(3.5 毫米 --> Type-C) let dot35mmToTypecAdapter = new Dot35mmToTypecAdapter(dot35mmHeadset) phone.insertTypeC(typecHeadset) // Type-C 耳機已插入 phone.insertTypeC(dot35mmToTypecAdapter) // 3.5mm 耳機已插入
Decorator Pattern
)真實場景:
先建一個基礎版本的手機,通過再加工對手機進行裝飾潤色,
描述:
裝飾者模式,容許你經過將對象包裝在裝飾器類的對象中,來動態更改對象在運行時的表現行爲
// 基礎 car 類 class SimpleCar { getCost () { return 10 } getDescription () { return 'Simple car' } } // 奧迪 class AudiCar { constructor (car) { this.car = car } getCost () { return this.car.getCost() + 6 } getDescription () { return this.car.getDescription() + ', Audi' } } // 寶馬 class BMWCar { constructor (car) { this.car = car } getCost () { return this.car.getCost() + 8 } getDescription () { return this.car.getDescription() + ', BMW' } } // 基礎車信息 let simpleCar = new SimpleCar() simpleCar.getCost() // 10 simpleCar.getDescription() // Simple car // 奧迪車信息 let audiCar = new AudiCar(simpleCar) audiCar.getCost() // 16 audiCar.getDescription() // Simple car, Audi // 寶馬車信息 let bmwCar = new BMWCar(simpleCar) bmwCar.getCost() // 18 bmwCar.getDescription() // Simple car, BMW
Facade Pattern
)真實場景:
如何讓手機開機?很簡單,「長按電源鍵」便可開機。實際上手機內部處理了不少邏輯才能實現它。這就是所謂的外觀模式,將複雜轉爲簡潔。
例如,開發時經常使用於瀏覽器兼容性處理的代碼。
描述:
爲複雜的子系統提供了簡化統一的界面
// DOM 綁定事件的兼容性處理 function addEventFacade (el, eve, fn) { if (el.addEventListener) { el.addEventListener(eve, fn, false) } else if (el.attachEvent) { el.attachEvent('on' + eve, fn) } else { el['on' + eve] = fn } }
Proxy Pattern
)真實場景:
你能夠直接去專賣店買手機(某海外品牌),也能夠經過代購幫你買手機(這樣價格會便宜些)。讓代購買手機這種方式,就是所謂的代理模式。
js
原生也支持代理模式 Proxy
描述:
用一個對象來表示另外一個對象的功能
class Phone { constructor () { this.phoneName = 'Mate 90' // 手機名 this.screen = '6.21' // 尺寸 this.color = 'black' // 顏色 this.chip = 'kirin' // 芯片 } } class ProxyPerson { constructor(target) { this.target = target } buyPhone (phoneModel) { this.target.buyPhone(phoneModel) } } class Person { buyPhone (phoneModel) { console.log('哈哈,買到了: ', phoneModel.phoneName) } } let proxyPerson = new ProxyPerson(new Person()) proxyPerson.buyPhone(new Phone()) // 哈哈,買到了: Meta 90
Strategy Pattern
)真實場景:
商店打折促銷,某商品買 1
件原價,2
件 8
折,3
件 7
折
描述:
在代碼運行時,根據不一樣狀況切換不一樣的策略(方法)
// 策略組 const strategies = { num1 (price) { return price * 1 }, num2 (price) { return price * 0.8 }, num3 (price) { return price * 0.7 }, } // 獲取折後總價 function getDiscountPrice (n, price) { let _key = `num${n}` let discountedForItem = strategies[_key](price) return discountedForItem * n } getDiscountPrice(1, 10) // 10 getDiscountPrice(2, 10) // 16 getDiscountPrice(3, 10) // 21
State Pattern
)描述:
在狀態更改時更改類的行爲(方法)
class Discount { constructor () { // 當前狀態 this.currentState = '' // 全部狀態 this.states = { typeA () { console.log('typeA 1') }, typeB () { console.log('typeB 0.8') }, typeC () { console.log('typeC 0.7') }, } } // 更新當前狀態 setState (_currentState) { this.currentState = _currentState return this } // 開始計算 compute () { let currentStateHandler = this.states[this.currentState] currentStateHandler && currentStateHandler() return this } } let discount = new Discount() discount .setState('typeA') .compute() // typeA 1 .setState('typeC') .compute() // typeC 0.7 .setState('typeB') .compute() // typeB 0.8
歡迎關注無廣告文章公衆號:學前端