編 輯:老彭
來(lái) 源:大數(shù)據(jù)架構(gòu)師
彭友們好,我是老彭。最近看了一本好書,人稱Bob大叔的Robert C.Martin寫的《整潔架構(gòu)之道》。一直在數(shù)據(jù)和架構(gòu)領(lǐng)域追尋大佬的老彭那讀的是津津有味啊~~~
我在帶團(tuán)隊(duì)的時(shí)候經(jīng)常跟同事們說(shuō)的一句話就是:只要你在寫代碼,無(wú)論用的是什么語(yǔ)言,做的是什么業(yè)務(wù),一定要鍛煉自己從架構(gòu)師的視角思考問(wèn)題。這也是老彭為啥給公眾號(hào)取名大數(shù)據(jù)架構(gòu)師的原因之一。
是否擁有架構(gòu)師的視角,決定了你是否能編程水平的瓶頸。
好,話不多說(shuō),開嘮今天的嗑。
01 少林無(wú)上內(nèi)功心法易筋經(jīng):高內(nèi)聚、低耦合老彭還在本科學(xué)習(xí)的時(shí)候就聽說(shuō)過(guò)高內(nèi)聚,低耦合(High Cohesion and Low Coupling)。當(dāng)時(shí)只是單純的覺(jué)得不明覺(jué)厲,根本沒(méi)明白這句話的威力有多大。
再次聽到這句話是在考高項(xiàng)的時(shí)候,那是老彭已經(jīng)是一個(gè)3、4年經(jīng)驗(yàn)的SQL Boy了。之前基本上都沉浸在存儲(chǔ)過(guò)程、提數(shù)sql、報(bào)表開發(fā)等繁重的工作中,全憑本能在寫代碼,猶如剛上少林寺天天挑水打雜的小和尚,每一個(gè)任務(wù)就像是一次新的挑水任務(wù),基本上都得重來(lái)。
老彭愚鈍,一個(gè)復(fù)雜的報(bào)表經(jīng)常能突破上千行代碼,一直覺(jué)得這樣不對(duì),但又不知道該怎么辦。
再次聽老師講解高內(nèi)聚、低耦合其中的奧秘,老彭覺(jué)得這簡(jiǎn)直就是上天來(lái)幫助我從繁重的體力勞動(dòng)中解脫的易筋經(jīng),一語(yǔ)道破為何那時(shí)老彭的代碼又臭又長(zhǎng)的原因。
所謂內(nèi)聚性,就是一個(gè)軟件單位內(nèi)部的關(guān)聯(lián)緊密程度。
所謂耦合性,則是強(qiáng)調(diào)兩個(gè)或多個(gè)軟件單位之間的關(guān)聯(lián)緊密程度。
我們要盡可能讓一個(gè)軟件單位內(nèi)的關(guān)聯(lián)更緊密,或者說(shuō)把關(guān)聯(lián)緊密的功能放在一個(gè)軟件單位內(nèi)實(shí)現(xiàn),這樣能避免多余的重復(fù)和交互。
同時(shí)我們還要盡量讓兩個(gè)或者多個(gè)軟件單位之間的關(guān)聯(lián)緊密程度不要那么高,減少相互依賴,這樣能最大化減少一個(gè)軟件單位對(duì)另一個(gè)軟件單位的影響,把錯(cuò)誤或者運(yùn)維工作控制在有限范圍內(nèi)。
轉(zhuǎn)頭老彭就回去開始重構(gòu)代碼,把各種sql、報(bào)表中共性的部分都提煉出來(lái),構(gòu)造函數(shù),建設(shè)基礎(chǔ)報(bào)表,甚至回過(guò)頭到數(shù)倉(cāng)的整合層和匯總層重構(gòu)ETL程序,優(yōu)化各層的數(shù)據(jù)模型。
將數(shù)倉(cāng)各層、函數(shù)、共性報(bào)表進(jìn)行充分解耦,在每層、每個(gè)函數(shù)、每個(gè)共性報(bào)表中把它們需要解決的問(wèn)題盡量獨(dú)立完成,使之盡量?jī)?nèi)聚,之間通過(guò)定義數(shù)據(jù)標(biāo)準(zhǔn)進(jìn)行交互。
這樣一下如同撥開云霧見(jiàn)天明,開發(fā)和運(yùn)維的工作量頓時(shí)瞬間降低了一個(gè)數(shù)量級(jí)別。
如果業(yè)務(wù)系統(tǒng)某張表的字段名字或者長(zhǎng)度發(fā)生變化,老彭只需要在數(shù)倉(cāng)的第二層進(jìn)行調(diào)整就行了,由于每層做了充分解耦,后面用的字段已經(jīng)統(tǒng)一為成標(biāo)準(zhǔn)字段、新的數(shù)據(jù)模型,所以后續(xù)代碼根本無(wú)需做任何調(diào)整。
即便是有新表、新字段,由于代碼已經(jīng)層層抽象,數(shù)據(jù)逐層落地,因此只需要在需要調(diào)整的地方略微修改即可。
當(dāng)老彭花費(fèi)了不少時(shí)間調(diào)整之后,終于能按時(shí)下班了。這是老彭第一次嘗到架構(gòu)思維的甜頭。
也需有人會(huì)說(shuō)老彭真笨,學(xué)校都教過(guò)了,照做就行唄,還需要花這么多時(shí)間先錯(cuò)后改,浪費(fèi)時(shí)間。但是老彭覺(jué)得,這也需就是紙上得來(lái)終覺(jué)淺,絕知此事要躬行吧。
從文字上來(lái)說(shuō),就短短的6個(gè)字,其中的奧妙還需自己慢慢體會(huì)。
02 無(wú)上神功太玄經(jīng):SOLID原則如果說(shuō)高內(nèi)聚、低耦合 太過(guò)于基礎(chǔ),那么就得請(qǐng)出如同金庸小說(shuō)里最強(qiáng)的無(wú)上神功太玄經(jīng)一般存在的SOLID原則:
這里不得不吐槽一下老外奇葩的取名方法,邏輯居然是首字母湊單詞
Bob大叔在《整潔架構(gòu)之道》里就披露了SOLID原則的由來(lái):
20世紀(jì)80年代后期,我在 USENET (古早版 Facebook )上和其他人就軟件設(shè)計(jì)原則辯論時(shí),便開始?xì)w納這些原則。多年來(lái),這些原則發(fā)生了一些變化。有些被刪除,有些被合并,還新增了一些。
2000年代初,這組原則的最終版確定,只是和我的順序有所不同。
2004年前后, Michael Feathers 給我發(fā)了封電子郵件,說(shuō)如果重新排列這些原則,它們的首字母可以拼成單詞 SOLID -﹣于是稱為 SOLID 原則。
SOLID夠奇葩了吧?更奇葩的是每個(gè)原則的名字...
網(wǎng)上有很多SOLID原則的解釋,這里老彭就不贅述了,只講講自己的一些體會(huì)。
①單一職責(zé)原則(SRP)定義為:一個(gè)模塊應(yīng)該對(duì)且僅對(duì)一類行為主體負(fù)責(zé)。
Bob大叔在書里也吐槽說(shuō)這個(gè)名字取得不好,導(dǎo)致很多人誤解了。其實(shí)老彭覺(jué)得叫做需求隔離原則,或者用戶隔離原則可能會(huì)更好。
因?yàn)檫@個(gè)原則其實(shí)大有來(lái)頭,是根據(jù)康威定律推導(dǎo)出來(lái)的。
康威定律:軟件的最佳系統(tǒng)架構(gòu)很大程度上取決于開發(fā)它的組織架構(gòu)。也就是說(shuō),企業(yè)組織架構(gòu)決定了系統(tǒng)架構(gòu)。
從這個(gè)視角上看,單一職責(zé)原則是從需求端視角(用戶、用戶群等某類行為主體)進(jìn)行隔離,不能將多個(gè)用戶、用戶群的需要放在同一個(gè)模塊里,這樣會(huì)導(dǎo)致某個(gè)用戶的需求發(fā)生變化,會(huì)導(dǎo)致該模塊的調(diào)整,進(jìn)而影響其他用戶的需求。
書里給了一個(gè)反例,彭友們一看就明白:
Employee類里的三個(gè)方法分別服務(wù)三個(gè)對(duì)象。一旦三個(gè)CXO中的任意一個(gè)人需求有變更,都會(huì)導(dǎo)致Employee類要修改,從而影響另外倆CXO的正常需要。
②開閉原則(OCP)定義為:對(duì)擴(kuò)展開放,對(duì)修改封閉。
是不是同樣有些拗口?其實(shí)很簡(jiǎn)單,老彭帶你從面對(duì)需求變更的視角就瞬間明白了。
這個(gè)原則是為了面對(duì)需求變更而存在的。當(dāng)面臨需求變更時(shí),我們盡量減少對(duì)現(xiàn)有程序的修改,用新增代碼完成變更的需求。減少改代碼,就能減少不必要的bug。
怎么實(shí)現(xiàn)呢?書里舉了一個(gè)生成財(cái)務(wù)報(bào)表的例子:
注:>代表接口,
其設(shè)計(jì)思想是將報(bào)表生成拆分為報(bào)表數(shù)據(jù)計(jì)算和報(bào)表展示兩個(gè)獨(dú)立的職責(zé),再加上必要的控制模塊和數(shù)據(jù)庫(kù)模塊,之間都用接口進(jìn)行隔離,這樣就能確保相互不會(huì)受到影響。
總體上既能保證非常強(qiáng)的擴(kuò)展性,又能很好地隔離了各個(gè)功能之間的相互影響。在本例中,interactor把核心業(yè)務(wù)處理好了,其他細(xì)枝末節(jié)交的任務(wù)給另外3個(gè)模型實(shí)現(xiàn),最符合OCP原則。
這里有兩個(gè)設(shè)計(jì)技巧:依賴方向的控制和信息隱藏。接口就是完成依賴方向控制的重要手段之一。接口和data mapper也是信息隱藏重要手段。
在這里個(gè)例子,一旦數(shù)據(jù)庫(kù)發(fā)生變化,只需要改一下data mapper或者接口就行,interactor內(nèi)部完全不受任何影響。
為了讓大家更容易理解,老彭給這個(gè)原則取個(gè)外號(hào),叫堅(jiān)決不改代碼原則,或者叫只增不改原則。
③里氏替換原則(LSP)定義為組件必須遵循可以相互替換的約定。是不是看不明白?
其實(shí)也非常簡(jiǎn)單,這是從設(shè)計(jì)子對(duì)象的視角上進(jìn)行設(shè)計(jì)的原則。
書里給的例子也很清晰,Personal License 和Business License繼承Licenes,并且在Billing調(diào)用的時(shí)候,可以直接替換Licenes,而不會(huì)出現(xiàn)不適當(dāng)?shù)膯?wèn)題。
書里同時(shí)給了一個(gè)反例,就是矩形和正方形。正方形雖然是矩形的一種,但是他們是有區(qū)別的。矩形的兩個(gè)參數(shù)長(zhǎng)寬都可以隨便變,但是正方形雖然也能繼承長(zhǎng)寬,但是這倆必須得同時(shí)變才行。因此正方形不能成為矩形的子類型。
老彭覺(jué)得還是叫龍生龍?jiān)瓌t比較符合拗口的歷史替換原則比較合適。畢竟龍生龍鳳生鳳,老鼠的兒子會(huì)打洞。
④接口隔離原則(ISP)定義為恰當(dāng)?shù)厥褂媒涌冢苊獠槐匾囊蕾嚒?/p>
這個(gè)就很好理解了。書里也給出了生動(dòng)的例子:
當(dāng)user1調(diào)用OPS類里的op1時(shí),會(huì)強(qiáng)行依賴op2和op3,但是很明顯,op2和op3分屬user2和user3,跟user1完全沒(méi)啥關(guān)系。這樣就造成了不必要的依賴。
解決辦法是給每個(gè)op(操作)都做一個(gè)獨(dú)立的接口,把操作隔離到接口中,這樣就實(shí)現(xiàn)了解耦。
這個(gè)原則的名字還是挺貼切的,老彭還是給它取個(gè)外號(hào)吧,就叫人人有份,大家別搶原則
⑤依賴反轉(zhuǎn)原則(DIP)定義為源代碼應(yīng)該多依賴抽象類型而不是具體實(shí)現(xiàn)。
名字同樣的拗口,定義同樣的不知所云。
其實(shí)換個(gè)說(shuō)法就好多了:這是從引用、繼承的視角進(jìn)行設(shè)計(jì)的原則。為了保證代碼不必改來(lái)改去(更穩(wěn)定),我們需要做到以下幾點(diǎn):
1.不要引用易變的具體實(shí)現(xiàn)類;
2.不要衍生、繼承易變的具體實(shí)現(xiàn)類;
3.不要覆蓋類中具體實(shí)現(xiàn)函數(shù);
4.永遠(yuǎn)不要直接引用任何具體實(shí)現(xiàn)和易變類的名字。
Bob大叔建議利用抽象工廠模式設(shè)計(jì)和管理依賴。如上圖所示,中間的曲線是架構(gòu)邊界,上面是抽象接口,下面是具體實(shí)現(xiàn)。
因?yàn)榭缭角€的箭頭方向和實(shí)際源代碼依賴的方向相反,所以該原則叫依賴反轉(zhuǎn)原則。這取名字的水平也是沒(méi)誰(shuí)了。
吐槽一下,這真的不是翻譯的問(wèn)題,就tmd是原作者的問(wèn)題。
老規(guī)矩,老彭給取個(gè)外號(hào),叫身份證原則,大家對(duì)外溝通的時(shí)候,都取一個(gè)不會(huì)改的身份證號(hào)就完了。至于你的真名、網(wǎng)名、筆名啥的,隨便取,隨便改。把一個(gè)獨(dú)立的個(gè)體,抽象成一個(gè)身份證號(hào),完美解決。
03 小結(jié)Bob大叔真的是我偶像!軟件領(lǐng)域的巨頭之一。架構(gòu)造詣超神,但是取名字的水平實(shí)在是令老彭無(wú)力吐槽
以上插圖、案例就來(lái)自于此書,強(qiáng)烈建議各位購(gòu)買這本書過(guò)來(lái)瞅瞅,必有收獲。
看完覺(jué)得寫得好的,不防打賞一元,以支持藍(lán)海情報(bào)網(wǎng)揭秘更多好的項(xiàng)目。