從設(shè)計(jì)原則,程序?qū)崿F(xiàn)目標(biāo)談?wù)勄岸顺S玫?0種設(shè)計(jì)模式和應(yīng)用場(chǎng)景
時(shí)間:2023-10-07 16:18:01 | 來(lái)源:網(wǎng)站運(yùn)營(yíng)
時(shí)間:2023-10-07 16:18:01 來(lái)源:網(wǎng)站運(yùn)營(yíng)
從設(shè)計(jì)原則,程序?qū)崿F(xiàn)目標(biāo)談?wù)勄岸顺S玫?0種設(shè)計(jì)模式和應(yīng)用場(chǎng)景:
總括:
近幾年前端技術(shù)迭代迅速,除了ES6,Vue,React,Angular,各種企業(yè)級(jí)框架也層出不窮,比如egg,umi,nuxt,next,koa等等,還有redux, vuex, Mobx, Flux,dva等狀態(tài)管理庫(kù),這些給前端開(kāi)發(fā)工作帶來(lái)了極大的簡(jiǎn)化,同時(shí)也給開(kāi)發(fā)者很大的學(xué)習(xí)挑戰(zhàn)。但是想要在前端架構(gòu)層面有一定的高度,這些都是必須要掌握的。除了這些,還要掌握工程化,自動(dòng)化,服務(wù)端等。那么這些類(lèi)庫(kù)或者框架剝離業(yè)務(wù)之后,其背后的設(shè)計(jì)思想以及遵循的底層程序原理是怎樣的呢?
很多開(kāi)發(fā)者對(duì)于知識(shí)的掌握,框架的應(yīng)用都很熟悉,但是總有一些角落感覺(jué)沒(méi)有深入進(jìn)去,這可能是因?yàn)閷?duì)于程序本身沒(méi)有一個(gè)全局的認(rèn)識(shí)。舉個(gè)例子(不識(shí)廬山真面目,只緣身在此山中),好比在一座山之中,很多人可以很清楚的知道山里的路,但可能不會(huì)知道路為什么是這個(gè)走向,要想了解山勢(shì),就要走出這座山,從更高的地方觀(guān)看一目了然。
所以做程序不僅要走得進(jìn)來(lái),更要能夠走得出去,用最簡(jiǎn)單的方式思考問(wèn)題,抓住程序的底層思維,設(shè)計(jì)出程序路徑,至于用什么方法實(shí)現(xiàn)反而不那么重要了。
一年前和一位后端高級(jí)架構(gòu)專(zhuān)家討論前端如何能夠快速上手開(kāi)發(fā)后端需求,當(dāng)時(shí)我覺(jué)得是先要學(xué)好java或者其他語(yǔ)言,因?yàn)檫@是開(kāi)發(fā)的基礎(chǔ),如果基礎(chǔ)語(yǔ)法和知識(shí)都不清楚的話(huà),怎么開(kāi)發(fā)呢。不過(guò)他說(shuō)真正的程序開(kāi)發(fā),不管是前端和后端,和語(yǔ)言的關(guān)聯(lián)程度并不是排在第一位得,真正排在第一位的是程序?qū)崿F(xiàn)目標(biāo)和實(shí)現(xiàn)模式。事實(shí)上也是如此,語(yǔ)言和框架有很多,但是這些都是開(kāi)發(fā)的工具,我們要掌握的是開(kāi)發(fā)的思想。體會(huì)并深入掌握這一點(diǎn)后,能少走很多彎路。
什么是程序
計(jì)算機(jī)程序是一組計(jì)算機(jī)能識(shí)別和執(zhí)行的指令,運(yùn)行于電子計(jì)算機(jī)上,滿(mǎn)足人們某種需求的信息化工具。
系統(tǒng)的三大因素:結(jié)構(gòu)化的數(shù)據(jù),操作數(shù)據(jù)的邏輯(增刪改查),展示數(shù)據(jù)(ui層)
程序?qū)崿F(xiàn)目標(biāo)(OOP)
程序執(zhí)行時(shí)通過(guò)順序,判斷,循環(huán)實(shí)現(xiàn)增刪改查,這就要求數(shù)據(jù)有一定的結(jié)構(gòu),才方便操作。
所以程序?qū)崿F(xiàn)的目標(biāo)是將宏觀(guān)事物抽象為結(jié)構(gòu)化的數(shù)據(jù),并覆以操作數(shù)據(jù)的方法。
數(shù)據(jù)結(jié)構(gòu)
常用的數(shù)據(jù)結(jié)構(gòu)有Set, Map, Tree, Array,鏈表,圖等等,而操作這些就要有對(duì)應(yīng)的方法,這些方法基于一定的實(shí)現(xiàn)準(zhǔn)則,就是我們所說(shuō)的設(shè)計(jì)原則。
設(shè)計(jì)原則
何為設(shè)計(jì)
- 按照哪一種思路或者標(biāo)準(zhǔn)實(shí)現(xiàn)功能
- 功能相同或,可以用不同設(shè)計(jì)方案來(lái)實(shí)現(xiàn)
- 伴隨著需求增加,設(shè)計(jì)的作用才能體現(xiàn)出來(lái)
程序設(shè)計(jì)原則舉例(參考UNIX/LINUX設(shè)計(jì)哲學(xué))
- 小即是美
- 讓每個(gè)程序只做一件事情
- 快速建立原型
- 舍棄高效率而取可移植性
- 采用純文本存儲(chǔ)數(shù)據(jù)
- 充分利用軟件的杠桿效應(yīng)(軟件復(fù)用)
- 使用shell腳本提高杠桿效應(yīng)和可移植性
- 避免強(qiáng)制性的用戶(hù)界面
- 讓每個(gè)程序都成為過(guò)濾器
實(shí)際開(kāi)發(fā)中設(shè)計(jì)的五大原則(SOLID)
- 單一職責(zé)原則(SRP) 一個(gè)對(duì)象應(yīng)該只包含單一的職責(zé),并且該職責(zé)被完整地封裝在一個(gè)類(lèi)中
- 開(kāi)放封閉原則(OCP) 實(shí)體應(yīng)該對(duì)擴(kuò)展是開(kāi)放的,對(duì)修改是封閉的
- 里氏替換原則(LSP) 一個(gè)對(duì)象在其出現(xiàn)的任何地方,都可以用子類(lèi)實(shí)例做替換,并且不會(huì)導(dǎo)致程序的錯(cuò)誤
- 接口隔離原則(ISP) 接口隔離原則表明客戶(hù)端不應(yīng)該被強(qiáng)迫實(shí)現(xiàn)一些他們不會(huì)使用的接口
- 依賴(lài)倒置原則(DIP) 抽象不應(yīng)該依賴(lài)于細(xì)節(jié),細(xì)節(jié)應(yīng)當(dāng)依賴(lài)于抽象
說(shuō)明:?jiǎn)我宦氊?zé)原則(SRP),開(kāi)放封閉原則(OCP) 在javascript開(kāi)發(fā)中應(yīng)用非常廣泛, 里氏替換原則(LSP),接口隔離原則(ISP),依賴(lài)倒置原則(DIP) 在javascript種應(yīng)用較少,在TypeScript中可以體現(xiàn)。1,單一職責(zé)原則
- 一個(gè)程序只針對(duì)一件事情
- 如果功能過(guò)于復(fù)雜,每個(gè)部分保持獨(dú)立
2,開(kāi)放封閉原則
- 對(duì)擴(kuò)展開(kāi)放,對(duì)修改封閉
- 增加需求時(shí),擴(kuò)展新代碼,而非修改已有代碼
- 這是軟件設(shè)計(jì)的最終目標(biāo)
3,李氏置換原則
子類(lèi)能覆蓋父類(lèi)
父類(lèi)出現(xiàn)的地方子類(lèi)就能出線(xiàn)
4,接口獨(dú)立原則
保持接口的單一獨(dú)立,避免出現(xiàn)旁接口(不是100%,像外觀(guān)模式,后面詳細(xì)介紹)
類(lèi)似于單一職責(zé)原則,但是這里更注重于接口
5,依賴(lài)倒置原則
面向接口編程,依賴(lài)于抽象而不依賴(lài)于具體
使用方法只關(guān)注接口而不關(guān)注具體類(lèi)的實(shí)現(xiàn)
對(duì)比分析
開(kāi)閉原則(OCP)是面向?qū)ο笤O(shè)計(jì)原則的基礎(chǔ)也是整個(gè)設(shè)計(jì)的一個(gè)終極目標(biāo),而依賴(lài)倒置原則(DIP )則是實(shí)現(xiàn)OCP原則的一個(gè)基礎(chǔ),換句話(huà)說(shuō)開(kāi)閉原則(OCP)是你蓋一棟大樓的設(shè)計(jì)藍(lán)圖,那么依賴(lài)倒置原則就是蓋這棟大樓的一個(gè)鋼構(gòu)框架。
從設(shè)計(jì)到模式
設(shè)計(jì)和模式是分開(kāi)的,有了設(shè)計(jì)才產(chǎn)出了模式。設(shè)計(jì)是指導(dǎo)思想,模式是根據(jù)指導(dǎo)思想抽象出來(lái)的一些模板。
設(shè)計(jì)模式分類(lèi)
從功能上劃分為三類(lèi):
創(chuàng)建型,組合型,行為型具體到每個(gè)類(lèi)型又細(xì)分如下, 如下圖:
UML類(lèi)圖
在講設(shè)計(jì)模式之前,介紹下UML類(lèi)圖:詳細(xì)參見(jiàn)UML類(lèi)圖
在UML的靜態(tài)機(jī)制中類(lèi)圖是一個(gè)重點(diǎn),它不但是設(shè)計(jì)人員關(guān)心的核心,更是實(shí)現(xiàn)人員關(guān)注的核心。建模工具也主要根據(jù)類(lèi)圖來(lái)產(chǎn)生代碼。
- 類(lèi)是具有相似結(jié)構(gòu)、行為和關(guān)系的一組對(duì)象的描述符。
- 類(lèi)是面向?qū)ο笙到y(tǒng)中最重要的構(gòu)造塊。
- 類(lèi)圖顯示了一組類(lèi)、接口、協(xié)作以及他們之間的關(guān)系。
- 類(lèi)的屬性、操作中的可見(jiàn)性使用+、#、-分別表示public、protected、private。
常見(jiàn)的設(shè)計(jì)模式和應(yīng)用場(chǎng)景
1,工廠(chǎng)模式
- 將new操作單獨(dú)封裝
- 遇到new時(shí),就要考慮是否該使用工廠(chǎng)模式
宏觀(guān)例子:購(gòu)買(mǎi)漢堡時(shí)直接取餐點(diǎn)餐,不需要做,商店封裝了做漢堡的工作
UML類(lèi)圖應(yīng)用場(chǎng)景:jQuery $('div')、React.createElement、vue異步組件工廠(chǎng)模式構(gòu)造函數(shù)和創(chuàng)建者分離,符合開(kāi)放封閉原則2,單例模式
- 系統(tǒng)中被唯一使用
- 一個(gè)類(lèi)只有一個(gè)實(shí)例
比如登錄框和購(gòu)物車(chē)
UML類(lèi)圖:
應(yīng)用場(chǎng)景:jquery只有一個(gè)$
模擬登錄框
vuex和redux的store
單例模式符合單一職責(zé)原則,只有一個(gè)實(shí)例化對(duì)象。3,適配器模式
- 就接口格式和使用者不兼容
- 中間加一個(gè)適配器轉(zhuǎn)換接口
應(yīng)用場(chǎng)景:封裝接口,vue中的computed
適配器模式就接口和使用者進(jìn)行了分離,符合開(kāi)放封閉原則4,裝飾器模式
- 為對(duì)象添加新功能
- 不改變其原有的功能和架構(gòu)
應(yīng)用場(chǎng)景:ES7裝飾器, core-decorators(第三方庫(kù))
也可以去裝飾類(lèi)的方法
裝飾器不能用于函數(shù),因?yàn)楹瘮?shù)存在提升裝飾器模式將現(xiàn)有對(duì)象和裝飾器分離,兩者獨(dú)立存在,符合開(kāi)放封閉原則5,代理模式
代理模式提供限制后一摸一樣的接口
舉例:科學(xué)上網(wǎng),訪(fǎng)問(wèn)github
明星經(jīng)紀(jì)人
應(yīng)用場(chǎng)景 : 網(wǎng)頁(yè)事件代理,es6 proxy
代理模式原始對(duì)象和代理對(duì)象獨(dú)立,符合開(kāi)放封閉原則代理模式,適配器模式,裝飾器模式對(duì)比分析:
代理模式:經(jīng)限制之后提供一模一樣的接口,代理功能適配器模式:原有的方法不能用,轉(zhuǎn)換功能裝飾器模式:原有的方法還要使用,擴(kuò)展功能6,外觀(guān)模式
- 為子系統(tǒng)中的一組接口提供了一個(gè)高層接口
- 使用者使用高層接口
比如去一家醫(yī)院,掛號(hào),取藥,劃價(jià)等集中到一個(gè)部門(mén),病人只需要到部門(mén)即可
應(yīng)用場(chǎng)景:參數(shù)不同時(shí),使用一樣
外觀(guān)模式不符合單一職責(zé)原則,不符合開(kāi)放封閉原則,不能濫用7,觀(guān)察者模式
舉例:訂牛奶
應(yīng)用場(chǎng)景:- 網(wǎng)頁(yè)事件綁定
- Promise
- Jquery callbacks
- node.js自定義事件
- redux
- vue、React生命周期觸發(fā)
- vue watch
- node多進(jìn)程通訊
這里不在寫(xiě)具體代碼,觀(guān)察者模式是前端開(kāi)發(fā)中最常見(jiàn)的設(shè)計(jì)模式,應(yīng)用比較廣泛,核心是發(fā)布訂閱。
觀(guān)察者模式主題和觀(guān)察者分離,符合開(kāi)放封閉原則8,迭代器模式
- 順序訪(fǎng)問(wèn)一個(gè)集合
- 使用者無(wú)需知道集合的內(nèi)部結(jié)構(gòu)(封裝)
說(shuō)明:迭代器模式生成了一種訪(fǎng)問(wèn)機(jī)制,只要符合這種訪(fǎng)問(wèn)機(jī)制的就可以被該迭代器遍歷。
應(yīng)用場(chǎng)景:ES6的iterator
es6為什么要設(shè)計(jì)iterator由于es6中有序集合的數(shù)據(jù)類(lèi)型已經(jīng)很多,比如:
Array, Map, Set, String, TypeArray, arguments, NodeList
這些數(shù)據(jù)類(lèi)型都具有Symbol.iterator屬性,基于這個(gè)共同屬性,ES6提供了一個(gè)iterator迭代器。
手動(dòng)實(shí)現(xiàn)這類(lèi)數(shù)據(jù)的遍歷:
function each(data){ let iterator = data[Symbol.iterator] let item = {done:false} while(!item.done){ item = iterator.next() } if(!item.done){ console.log(item.value) }}
es6提供了for...of...來(lái)遍歷這種數(shù)據(jù)集合
和generator的關(guān)系如果不了解迭代器這種設(shè)計(jì)模式,對(duì)于generator和iterator是不太好理解的,比較抽象。
對(duì)比generator的使用
function* foo() { yield 1; yield 2; yield 3; yield 4; yield 5; return 6;} for (let v of foo()) { console.log(v);}// 1 2 3 4 5
foo()執(zhí)行過(guò)后生成了可供for...of...遍歷的數(shù)據(jù)集合,說(shuō)明generator是用來(lái)生成具有Symbol.iterator屬性的數(shù)據(jù)集合,generator內(nèi)部的yield就是該數(shù)據(jù)集合的每一項(xiàng),而這個(gè)數(shù)據(jù)項(xiàng)還可以是同類(lèi)數(shù)據(jù)集合的嵌套。
任何數(shù)據(jù)結(jié)構(gòu)只要有 Iterator 接口,就可以被
yield*
遍歷
function* foo() { yield 2; yield 3; return "foo";} function* bar() { yield 1; var v = yield* foo(); console.log("v: " + v); yield 4;} var it = bar(); it.next()// {value: 1, done: false}it.next()// {value: 2, done: false}it.next()// {value: 3, done: false}it.next();// "v: foo"// {value: 4, done: false}it.next()// {value: undefined, done: true}
迭代器模式中迭代器對(duì)象和目標(biāo)對(duì)象分離,迭代器將使用者和目標(biāo)對(duì)象隔離開(kāi),符合開(kāi)放封閉原則。9,原型模式
從原型創(chuàng)建對(duì)象(clone自己,生成一個(gè)新對(duì)象)
和js中的prototype不同
應(yīng)用場(chǎng)景:Object.create(prototype)(比較像原型模式,java中的clone是原型模式)
10,狀態(tài)模式
- 一個(gè)對(duì)象有狀態(tài)變化
- 每次狀態(tài)變化都會(huì)觸發(fā)一個(gè)邏輯
- 不能總用if...else...控制
例子:紅綠燈的變化
應(yīng)用場(chǎng)景:有限狀態(tài)機(jī), Promise
javascript-state-machine
其他設(shè)計(jì)模式
除了以上常用的10種設(shè)計(jì)模式,還有一些不常見(jiàn)的設(shè)計(jì)模式(在前端中應(yīng)用較少),這里不再具體介紹
總結(jié):
設(shè)計(jì)模式在一些庫(kù)和框架中應(yīng)用是很廣泛的,比如工廠(chǎng)模式,觀(guān)察者模式,單例模式,適配器模式,外觀(guān)模式,代理模式等等,掌握設(shè)計(jì)模式對(duì)于一些庫(kù)源碼的理解 以及他人代碼的理解是很有幫助的。設(shè)計(jì)模式是從原則而來(lái),是大量開(kāi)發(fā)者通過(guò)大量實(shí)踐得出的較為友好的程序?qū)崿F(xiàn)方式,像es6中的很多新語(yǔ)法其實(shí)是設(shè)計(jì)模式的具體體現(xiàn)。
在實(shí)際開(kāi)發(fā)過(guò)程中,刻意的使用和模仿某種設(shè)計(jì)模式,對(duì)于代碼的可讀性,性能都是很有幫助的,掌握設(shè)計(jì)模式之后,我們也可以更深入的理解es6中一些語(yǔ)法的含義,比如iterator, generator, decorator, proxy等。而如果在沒(méi)有了解設(shè)計(jì)模式的情況下,在對(duì)es6的理解上會(huì)相對(duì)困難。
可以通過(guò)閱讀一些經(jīng)典的lib,從設(shè)計(jì)模式方向了解其設(shè)計(jì)思路,進(jìn)而提升設(shè)計(jì)能力。
關(guān)鍵詞:設(shè)計(jì),模式,用場(chǎng),實(shí)現(xiàn),程序,原則,目標(biāo)