深入淺出JavaScript前端中的設計模式
目錄
- 關于設計模式
- 七種常見的設計模式
- 單例模式
- 工廠模式
- 適配器模式
- 裝飾器模式
- 策略模式
- 觀察者模式
- 發布-訂閱模式
關于設計模式
軟件設計模式,又稱設計模式,是一套被反復使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結。它描述了在軟件設計過程中的一些不斷重復發生的問題,以及該問題的解決方案。也就是說,它是解決待定問題的一系列套路,是前輩們的代碼設計經驗的總結,具有一定的普遍性,可以重復使用。其目的是為了提高代碼的可重用性、代碼的可讀性和代碼的可靠性。
簡單地說就是一些通用的代碼編寫方式,它是經過不斷考驗得出的一些總結道理,按照這樣的模式去編寫我們的代碼,沿著前人留下來的經驗,我們就可以編寫出詩一般的代碼。
關于設計模式,我們需要知道五大基本原則(SOLID):
(1)單一職責原則:一個類,應該僅有一個引起它變化的原因,簡而言之,就是功能要單一。
(2)開放封閉原則:對擴展開放,對修改關閉。
(3)里氏替換原則:基類出現的地方,子類一定出現。兩個字總結-繼承。
(4)接口隔離原則:一個接口應該是一種角色,不該干的事情不敢,該干的都要干。簡而言之就是降低耦合、減低依賴。
(5)依賴翻轉原則:針對接口編程,依賴抽象而不依賴具體。所編寫的對象不應該跟具體的實例掛鉤,應該更偏抽象的概念。
更具體地描述設計模式的好處,有以下幾點:
①良好的封裝,不會讓內部變量污染外部
②封裝好的代碼可以作為一個模塊給外部調用。外部無需了解細節,只需按約定的規范調用。
③對擴展開放,對修改關閉,即開放關閉原則。外部不能修改內部代碼,保證了內部的正確性;又留出擴展接口,提高了靈活性。
像我們常用的各大框架,如React,Vue等都有不同設計模式的應用,Vue中使用了觀察者模式和發布-訂閱模式。
七種常見的設計模式
設計模式一共分為3大類23種,這里主要介紹常用的幾種。
①創建型模式:單例模式、工廠模式、建造者模式;
②結構型模式:適配器模式、裝飾器模式、代理模式;
③行為型模式:策略模式、觀察者模式、發布訂閱模式、職責鏈模式、中介者模式。
單例模式
單例模式:一個類只有一個實例,并提供一個訪問他的全局訪問點,即一個類只生成一個唯一的實例。
我們在一個類中聲明屬性instance,當調用函數getInstance時,我們判斷instance是否已經存在實例,若存在則訪問該instance對象,若不存在則創建。
class Singleton { let _instance = null; static getInstance() {if (!Singleton._instance) { Singleton.instance = new Singleton()}// 如果這個唯一的實例已經存在,則直接返回return Singleton._instance }}const s1 = Singleton.getInstance()const s2 = Singleton.getInstance()
Vuex就是一個典型的單例模式使用案例, store對象就是一個單例對象。
根據其功能代碼,我們可以看出單例模式的優劣點都在哪里。
優點: 節約資源,保證訪問的一致性。
缺點: 擴展性不友好,因為單例模式一般自行實例化,沒有接口。
工廠模式
這個模式我們就非常常用了,聲明一個class,然后根據傳進來的參數去生成對應的實例對象,就是所謂的工廠模式。每一個類就像一個已經開設好的工廠,我們只需要告訴我們的需求,它就會生成我們想要的一個對象返回。
class Restaurant{ constructor(){this.menuData = {}; } // 獲取菜品 getDish(dish){if(!this.menuData[menu]){ console.log("菜品不存在,獲取失敗"); return;}return this.menuData[menu]; }, // 添加菜品 addMenu(menu,description){if(this.menuData[menu]){ console.log("菜品已存在,請勿重復添加"); return;}this.menuData[menu] = menu; } // 移除菜品 removeMenu(menu){if(!this.menuData[menu]){ console.log("菜品不存在,移除失敗"); return;}delete this.menuData[menu]; },}class Dish{ constructor(name,description){this.name = name;this.description = description; } eat(){console.log(`I"m eating ${this.name},it"s ${`this.description); }}
優點:
- 良好的封裝,訪問者無需了解創建過程,代碼結構清晰。
- 擴展性良好,通過工廠方法隔離了用戶和創建流程,符合開閉原則。
- 解耦了高層邏輯和底層產品類,符合最少知識原則,不需要的就不要去交流;
缺點:
缺點就是如果我們的類定義太過抽象復雜了,會出現閱讀性的問題。
適配器模式
這個模式也很好理解,相當于我們平時使用的一些產品,如投影儀之類的,如果我們的電線無法適配到我們的屏幕,我們就需要借助一個中間的適配器,讓兩者可以溝通起來。
interface bookDataType1 { book_id: number; status: number; create: string; update: string;}interface bookDataType2 { id: number; status: number; createTime: number; updateAt: string;}interface bookDataType3 { book_id: number; status: number; createTime: number; updateAt: number;}const getTimeStamp = function (str: string): number { //.....轉化成時間戳 return timeStamp;};//適配器export const bookDataAdapter = { adapterType1(list: bookDataType1[]) { const bookDataList: bookData[] = list.map((item) => { return {book_id: item.book_id,status: item.status,createAt: getTimeStamp(item.create),updateAt: getTimeStamp(item.update), }; }); return bookDataList; }, adapterType2(list: bookDataType2[]) { const bookDataList: bookData[] = list.map((item) => { return {book_id: item.id,status: item.status,createAt: item.createTime,updateAt: getTimeStamp(item.updateAt), }; }); return bookDataList; }, adapterType3(list: bookDataType3[]) { const bookDataList: bookData[] = list.map((item) => { return {book_id: item.book_id,status: item.status,createAt: item.createTime,updateAt: item.updateAt, }; }); return bookDataList; },};
優點: 可以使原有邏輯得到更好的復用,有助于避免大規模改寫現有代碼,為了不改動原有的代碼而做出的一種妥協;
缺點:會讓系統變得零亂,明明調用 A,卻被適配到了 B,如果濫用,那么對可閱讀性不太友好。簡而言之搞復雜了,所以通常建議不要出現以上這樣的格式問題,應該跟后端溝通好數據。
裝飾器模式
典型的大腸包小腸,當前使用的對象無法滿足我們的全部需求,于是乎我們建一個新的類,再把這個對象在類中進行擴展,再生成一個新的對象。
策略模式
這個是相當相當常用,而且很好用的一個設計模式,可以讓我們根據不同的選擇去實現對應的功能,省略了大量的if,else。
比如我們現在有一個需求判斷,比如我現在要根據別人給我的不同食材去制造料理,最暴力常規那就是if,else多寫幾個就解決了。但是這里如果我們用策略模式就可以用很清晰,很簡潔的代碼去解決這個問題。
if ( "food" == "蘋果") { 水煮()} else if ("food" == "胡蘿卜") { 炒了()} else if ("food" == "魚") { 清蒸()} else if ("food" == "豬肉") { 炸了()} else if ("food" == "牛肉") { 烤了()} else { 生吃()}// 用了策略模式,看起來舒服多了let wayObj = { "蘋果": 水煮(), "胡蘿卜": 炒了(), "魚": 清蒸(), "豬肉": 炸了(), "牛肉": 烤了(), "不知道": 生吃()}
觀察者模式
這個模式從名字就可以看出來它是干嘛的,觀察者重點就是觀察,有兩個對象,一個是觀察,一個是被觀察,被觀察發生了變化,那我們觀察的對象就可以知道這個變化。
觀察者模式有一個別名叫“發布-訂閱模式”,或者說是“訂閱-發布模式”,訂閱者和訂閱目標是聯系在一起的,當訂閱目標發生改變時,逐個通知訂閱者。我們可以用報紙期刊的訂閱來形象的說明,當你訂閱了一份報紙,每天都會有一份最新的報紙送到你手上,有多少人訂閱報紙,報社就會發多少份報紙,報社和訂報紙的客戶就是上面文章開頭所說的“一對多”的依賴關系。
// 觀察者模式 被觀察者Subject 觀察者Observer Subject變化 notify觀察者let observerIds = 0;// 被觀察者Subjectclass Subject { constructor() { this.observers = []; } // 添加觀察者 addObserver(observer) { this.observers.push(observer); } // 移除觀察者 removeObserver(observer) { this.observers = this.observers.filter((obs) => { return obs.id !== observer.id; }); } // 通知notify觀察者 notify() { this.observers.forEach((observer) => observer.update(this)); }}// 觀察者Observerclass Observer { constructor() { this.id = observerIds++; } update(subject) { // 更新 }}
發布-訂閱模式
其實上面也說了,跟觀察者模式是有異曲同工之妙的,但是它可以是一個一對多的關系,而且它需要一個中間人。
class Event { constructor() { this.eventEmitter = {}; } // 訂閱 on(type, fn) { if (!this.eventEmitter[type]) { this.eventEmitter[type] = []; } this.eventEmitter[type].push(fn); } // 取消訂閱 off(type, fn) { if (!this.eventEmitter[type]) { return; } this.eventEmitter[type] = this.eventEmitter[type].filter((event) => { return event !== fn; }); } // 發布 emit(type) { if (!this.eventEmitter[type]) { return; } this.eventEmitter[type].forEach((event) => { event(); }); }}
到此這篇關于深入淺出JavaScript前端中的設計模式的文章就介紹到這了,更多相關JS設計模式內容請搜索以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持!
相關文章:
1. 《javascript設計模式》學習筆記五:Javascript面向對象程序設計工廠模式實例分析2. JavaScript設計模式學習之代理模式3. javascript設計模式 ? 命令模式原理與用法實例分析4. javascript設計模式 ? 解釋器模式原理與用法實例分析5. javascript設計模式 ? 工廠模式原理與應用實例分析6. 《javascript設計模式》學習筆記二:Javascript面向對象程序設計繼承用法分析7. JavaScript設計模式--簡單工廠模式實例分析【XHR工廠案例】8. JavaScript設計模式--簡單工廠模式定義與應用案例詳解9. javascript設計模式 ? 外觀模式原理與用法實例分析10. javascript設計模式 ? 狀態模式原理與用法實例分析