洞察設計模式的底層邏輯

設計模式是開發同學經常聊到的話題,也經常被用到實際的開發項目中,熟練的人可以做到信手拈來,不熟悉的人陷入苦思冥想中。筆者認爲,不僅僅要掌握設計模式的用法,更要洞察設計模式的底層邏輯,只有那樣,才能做到遇到實際的問題可以使用合適的設計模式去解決。

一 你應該關注底層邏輯

1 設計模式的段子

段子一:你讓他給你講設計模式,他給你講故事,聽完後,又蹦又跳,樂壞了;看原著設計模式和實際寫代碼時,又是又蹦又跳,那是瘋了。

段子二:你讓他給你講設計模式,他給你講架構;你和他講架構,他和你講建築學;你和他講建築學,他和你講哲學……

上面兩個典型的段子,可以看到大家平時學習設計模式的無奈,故事聽懂了,但依然沒有掌握設計模式,甚至設計模式的類圖大家也畫得出,卻還是不能靈活掌握設計模式。究其根本的原因是沒有掌握設計模式的內核思想,只是知道設計模式的外在形式,相當於只學到了“招式”,沒有學到“內功”。

2 底層邏輯的本質

很多事物都有底層邏輯,當掌握了事物的底層邏輯之後,很多事就好辦了,如果你已經洞察到了最核心的規律,在實際工作中就只需要按照規律去執行就可以。例如,我們看到很多營銷文案讓人眼前一亮、歎爲觀止,如果要讓我們去寫這些營銷文案,一開始還找不到門道,寫出來的標題平平淡淡,不夠吸引人。那營銷文案背後的底層邏輯是什麼呢?我們看到很多文案如“激發學習潛能的四大策略”、“如何在10天內記憶5000+單詞”、“一文提示爲什麼你比別人差”……這些文案有的使用陳述手法,有的使用疑問手法,有的使用對比手法,有的使用感嘆手法……再往下挖掘,不管使用哪種手法,本質來講是命中了人的爽點或痛點,再用這個底層邏輯去看各種文案,有的命中你的痛點,比如你想記憶更多的單詞,現實記不住單詞;比如你想成功,但努力了很久還沒有效果……有的命中你的爽點:你花更少的錢就能獲得更好的服務;你不用出門就能賺到錢……當你洞察到了營銷文案背後的底層邏輯,你現在也可以寫出吸引眼球的文案,這就是底層邏輯的力量!

我們學習23種設計模式,它們被劃分成創建型設計模式、結構型設計模式、行爲型設計模式,這就像營銷文案的寫作手法一樣,那麼設計模式的底層邏輯到底是什麼呢?

二 設計模式的底層邏輯

1 設計模式的基石

平時我們在寫代碼的時候,經常見到如下三種類型的代碼:麪條型的代碼、過程式的代碼和麪向對象的代碼,這裏以一個例子來說明這三種類型的編碼特點。

  • 麪條型代碼就是所有邏輯堆砌在一起,就像寫一篇文章,不怎麼分段落。比如古代雕刻文字,在一塊木板上雕刻一首詩,如果詩人要把其中的一個修改下,那得重新雕刻這首詩。非常容易發現這種模式的缺點:耦合太嚴重,牽一髮而動全身。
  • 過程式代碼在麪條型代碼基礎上有了很大的進步,它遵循“自頂向下,逐步求精”的思想,把一個大問題劃分成若干個小問題,分而治之。對應上面雕刻詩的例子,詩是由若干個行組成的,如果每塊木板上只雕刻一行詩,萬一要改某個字,只用重新雕刻那一行就行,不用重新雕刻整首詩。但如果要修改多個字,而且在不同的行時,這種極端情況下整個首詩又得重新雕刻了。
  • 面向對象代碼換了一種思考方式,詩是由行組成的,行又是由一個個字組成的,這也即是活字印刷的思想,這些字還可以複用於其它不同的詩,複用性非常強。

從上面的例子可以看到,核心還是洞察到事物的結構和關係,首先回答的是what,而不是how。過程式就是過分強調了how,一開始就思考怎麼去做,過程式思維是以自己爲中心,導演了整個功能流程,自己承擔了太多自己不應該承擔的職責,整個設計就顯得不靈活。面向對象是從對象的角度去看問題,解決問題是由各個對象協作完成,設計模式的基石就是面向對象,脫離了面向對象去談設計模式那是耍流氓。

2 設計模式的鼻祖

設計模式有一本經典的書籍:《設計模式:可複用面向對象軟件的基礎》,在書中作者提到了一句話:“找到變化,封裝變化”,這纔是設計模式的底層邏輯。很多人忽視了這句話,反而去追尋各種模式的招式,遇到實際的問題又找不到合適的設計模式去解決了。“找到變化,封裝變化”非常精練地提示了設計模式的本質,細細品味這句話,再去看23種設計模式,每種設計模式都在應對變化的事,比如策略模式,具體的策略在變化;工廠模式,創建的對象在變化;模板模式,具體模板算法實現在變化……這就好比營銷文案的底層邏輯:命中了你的痛點或爽點,具體痛點和爽點是什麼需要去尋找。在實際問題中,需要我們去看什麼在變化,選擇哪種設計模式比較合適。

3 再談底層邏輯

再回過頭看底層邏輯,平時我們看到的現象只是現象層,核心是要洞察到事物的底層邏輯,只有那樣才能真正理解現象、運用規律,如果你不懂營銷文案背後的底層邏輯,你所有的勤奮都是低水平的重複,很難寫出高質量的營銷文案,偶爾一兩次起得了良好的效果你也不知道爲什麼能吸引人。設計模式也是一樣,你能熟悉地畫出各種模式的UML圖,可你依然還是用不好設計模式,本質還是沒有掌握設計模式的底層邏輯,只看到了設計模式的現象層的招式。設計模式的底層邏輯是“找到變化,封裝變化”,這裏就有兩個問題:什麼在變化,如何封裝變化,大師以爲我們都知道,所以並沒有講具體怎麼去尋找變化,怎麼去封裝變化。接下來具體談談怎麼去運用設計模式的底層邏輯。

三 設計模式要回答的兩個問題

1 什麼在變化

“找到變化,封裝變化”這句話,首先要回答的是什麼在變化,如果變化沒有找到,就不可能封裝變化。筆者這裏以對象生命週期的視角去看待對象的變化,對象是由創建而產生,然後被使用,最後是消亡。對象有三個不同維度的變化:對象結構的變化、對象規格的變化、對象行爲的變化。以對象結構變化爲例,對象的關係劃分成兩類:線性關係和非線性關係(樹和圖),在線性關係中,如何解決一個對象的變化不會影響到關聯的對象?在樹型結構中,如何解決不斷新增加對象的問題?在圖型結構中,如何解決用戶方便使用複雜系統的問題?

找到變化是最爲關鍵,不同的業務問題,遇到的變化問題也是不一樣的,核心是要找到這些變化。比如對象規格的變化,有數量的變化、類型的變化、外觀的變化,在實際編碼的過程中就要有這種思考,比如創建一個對象,再深入思想下,有沒有其它類型的對象?數量有沒有變化?……只有找到了這些變化,具體怎麼去封裝變化就是技術的問題,接下來討論如何封裝變化。

2 如何封裝變化

從封裝的類型上看,有數據的封裝、方法的封裝、類型的封裝等。就具體的封裝方法而言,常見的有配置項、接口、抽象方法、類、註解、插件等具體的手段,再往上看主要使用了繼承、組合的方法,再往上看封裝的原則,常見的原則有單一職責、開閉原則、依賴倒置、隔離原則……大部分人平時更多地關注如何封裝變化,並沒有深入去思考什麼在變化。

四 用底層邏輯推導結構型設計模式

1 尋找對象結構的變化

從UML看,對象之間的關係有依賴、泛化、組合、聚合,但就結構關係上看只有兩種,線性關係和非線性關係。線性關係比較簡單,就是一對一的關聯關係,非線性關係分成兩種:樹型關係和圖型關係。

關係結構有變化,意味着依賴發生了變化,比如線性關係中的變化,A依賴的B發生了變化,此時B變化了就會影響A,怎麼做到B的變化不影響A就是要考慮的問題。

2 應對線性變化

如上面所講,如果B發生了變化,由於A依賴B,則對象A也要改變化,如何減少對A的影響呢?這裏有兩種方法:一種是通過增加適配來解決,另一種是通過代理來解決。這兩種方法的要點都是一個對象不與變化的對象直接關聯,不管是適配還是代理,都是引入了第三方來與B關聯,第三方負責與B進行交互,B對A是沒有感知的。有的人馬上發現了一個問題,這不是把問題轉移到第三方上了嗎?乍一看,還真是這麼回事,如果我們再發散思考,如果除了A要與B關係,還有E、F……,如果B一改就關聯的所有對象就要變化,這種代價就比較高,如果只與第三方關係,只用改一個地方,成本要少得多。

3 應對非線性變化

非線性關係比線性關係要複雜,常見也有兩種方法:一種是通過註冊機制,另一種通過抽象層屏蔽複雜性。當一個對象包含多個對象時,如果直接去管理,需要承擔的職責太多,通過註冊機制就比較好解決,增加一個對象,是通過註冊機制主動告知對象。另外一種方法就是通過抽象層屏蔽複雜性,比如門面模式,在門面內把所有的複雜度都規避,對外提供簡潔的接口。

五 業務變化之道

設計模式還是要應用到實際的業務中才能發揮它的價值, Alan Shalloway 提到一個觀點:無法預測哪裏有變化,但能知道哪裏可能有變化。平時我們在做業務需求開發時,要有這種識別變化的意識,先不要陷入面向過程的思維中,不要一上來就考慮如何去實現,而是思考它是什麼,會有哪些變化,比如對象的數量、對象的外觀、對象的種類……當把這些思考清楚之後,才能設計得更合理。

比如筆者之前做清結算業務時,投資人理財到期後,會將本息金額的錢打給投資人,剛開始只有大華支付通道,這裏就要想到一個問題,大華支付只是一種具體的實現方式,還會有沒有其它的支付方式,如果有就要做抽象設計,設計一個通用的支付模板類,每接一種新的支付通道時,只用重寫模板類中的幾個方法即可,後續又接了民生銀行支付、連連支付。

六 對象設計之道

有了前面所講內容的鋪墊,這裏再深入總結下對象設計的一些思考。對象設計有三個問題:有哪些對象?對象之間的關係是怎樣的?對象的職責有哪些?當把這三個問題梳理清楚了,對象設計也就容易得多,也是面向對象分析與設計的核心。正常來講,我們知道結構決定功能,功能決定行爲,這是非常符合人的邏輯認識,但要想了解清楚對象的結構又是非常難的,就像新冠病毒的分子結構也不是那麼容易破解的,尤其是複雜業務,它所包含的業務對象並不那麼容易弄清楚它的結構。

我們可以反過來思考,當有一種業務場景時,先思考它的職責是什麼,再去思考應該由哪些業務對象去承擔,這也是典型的歸納思維。比如在優惠券業務中,它的業務活動就三個:建券、發券、用券,也就是任何一個優惠券系統,它要提供這三個最爲基礎的能力,這三個能力又對應到兩個業務對象:券批次和券實例,券批次相當於是券的模板,告知優惠券的預算有多少、券面額是多少、使用條件是什麼……,具體發放到用戶手上的纔是券實例。

當有了業務對象之後,就要通過用例去思考對象模型的所包含的屬性和方法有哪些,這個過程並不是一次就能完美完成的,而是通過多次打磨纔行,這裏面就要遵循一些原則,比如單一職責、開閉原則、依賴倒置的原則……,讓整個模型的可擴展性更好。

七 一個案例

最後拿一個案例來講,店鋪類目是賣家爲了方便買家有針對性地選購商品而對商品做出的歸類,比如上新類目,把最近30天上架的商品歸類在一起,方便買家查找。遇到的挑戰就是怎麼用一套業務模型去支持不同業務方高度定製化的需求,有的需求方要求有三級類目,有的業務方要求浮動的兩級類目,同時圈品方式也不一樣,有的業務方要求有自動圈選商品,圈選商品的條件還不一樣,如按價格圈選、按商品上架時間圈選、按評價圈選……

怎麼去設計這套模型呢,還是從店鋪類目的定義去看,店鋪類目至少包含兩個關鍵的要素:類目結構和類目圈品,因爲歸類產生了結構,商品產生了圈品,考慮到類目有不同的層級和圈品條件,所以第一版模型就很快設計出來了,從模型中可以看到能支撐業務的訴求,尤其是圈品條件中業務方可以自定義各種條件註冊到平臺上,看到這個設計,筆者內心還是欣慰的。

但在實現的過程中,發現了一些問題,如根類目和子類目,在業務模型中有這兩個概念,在代碼上也要有這兩個概念,正是引入了這兩個概念,代碼寫起來就比較麻煩,本身它們並沒有什麼區別,現在人爲地把它們區分開來,很多邏輯都要寫兩遍。筆者又進模型進行了優化,變成了第二版模型,這個模型就更簡單了。

這裏想談的兩點是要保證模型的簡潔性和降低技術複雜度,技術人喜歡鑽研技術,喜歡把一些學到的技術應用到項目中,這實際上是一種技術偏見,以爲這樣才能體現出技術複雜度和技術能力。複雜並不見得有技術含量,就像設計模式的底層規律,作者並沒有長篇大談,而是隻有8個字"找到變化,封裝變化",大道至簡就是這個道理,我們學習設計模式,不要爲了用設計模式而用,一定要思考爲什麼用,解決了什麼問題,這樣纔有價值。

作者 | 不拔

原文鏈接

本文爲阿里雲原創內容,未經允許不得轉載。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章