面试官:你不懂六大设计原则,回去等通知吧!
一、前言
不知道大家是否有這樣的體會,就是在學習設計模式的時候,看了很多書籍,也照著很多示例把每個模式挨個敲了幾遍,但過了一段時間后,就會忘了一大半。或者有的朋友嘗試在業務編碼中使用,卻越用越復雜,本來一個類幾個方法能搞定的業務,套用模式后會多出好多接口和類,所以用著用著就放棄了。我說的比較直接點,很多教材或博客中使用Animal、Fruit、Car這些例子來教設計模式,初衷是好的,但真沒多大用,甚至會誤人子弟。
最近筆者再次學習了設計模式(不知道是這些年的第多少次了),突然有了些感悟。我盡量用最簡單通俗的語言描述出我想表達的,有的觀點可能比較偏激或不太適合所有人,但如果大家能從這篇文章中GET到一兩個點,那也值了,哈哈。
????下面我先以我個人的想法簡單粗暴地理解一下設計模式的六大原則。Show Time!
二、六大原則
2.1、單一職責原則
官方解釋:單一職責原則(SRP:Single responsibility principle)又稱單一功能原則,面向對象五個基本原則(SOLID)之一。它規定一個類應該只有一個發生變化的原因。
大白話:一個類只有一個發生變化的原因,我只能說在業務編碼中不太可能,或很維做到。其實大家不必糾結于有幾個讓類發生變化的原因。個人建議你想實踐單一職責原則,最好先養成良好的編碼規范,如果你平時寫代碼,一個類里不管什么業務方法都往里塞,一個方法里嵌套著各種判斷,再好的原則也幫不也你。
實踐:和類名不相關的業務不要放在這個類里,和方法名不相關的代碼請拆成單獨的子方法。還有當你在定義一個接口、類或方法的時候,從業務的角度用心地多想一下:這段代碼放這里真的合適嗎?
2.2、里氏替換原則
官方解釋:里氏替換原則(Liskov Substitution Principle LSP)面向對象設計的基本原則之一。里氏替換原則中說,任何基類可以出現的地方,子類一定可以出現。LSP是繼承復用的基石,只有當衍生類可以替換掉基類,軟件單位的功能不受到影響時,基類才能真正被復用,而衍生類也能夠在基類的基礎上增加新的行為。
大白話:這個原則其實還是很好理解的,主要是約束子類的行為,要求其行為和基類保持一致,如果替換為子類后,程序運行不正常,則說明子類沒有按基類的預期實現業務,或者說子類不適合繼承這個基類,對不起,請找適合的基類繼承。
實踐:如果子類從一個基類繼承后,實現基類定義的虛方法時感覺很別扭痛苦的時候,請考慮一下是否一定要從這個基類繼承。
2.3、依賴倒置原則
官方解釋:依賴倒置原則(Dependence Inversion Principle)是程序要依賴于抽象接口,不要依賴于具體實現。簡單的說就是要求對抽象進行編程,不要對實現進行編程,這樣就降低了客戶與實現模塊間的耦合。
大白話:多用接口和抽象類,少用實現類(工具類除外)。大家看框架源碼的時候應該能感覺到,大神在實現框架的時候到處都是接口或抽象類,而自己的代碼卻是一大片的實現類,其實直接用實現類是沒有問題的。架構方法中有一句叫可擴展性,其實接口和抽象類就是實現可擴展性的基石。當需要擴展原有邏輯的時候,別人用接口和抽象類的直接加個新子類繼承下,你用實現類的到處一大片一大片的改,請回答我,有沒有?
實踐:寫代碼的時候多考慮擴展性,如果這段代碼以后擴展或變化的可能性很高,請用接口或抽象類封裝下,抽象出不變的接口,將變化的部分留給不同的實現類。
2.4、接口隔離原則
官方解釋:接口隔離原則(Interface Segregation Principle,)使用多個專門的接口比使用單一的總接口要好。一個類對另外一個類的依賴性應當是建立在最小的接口上的。
大白話:這個原則和單一職責原則有點象,好吧,其實很難區分的。主要區分點在“職責”和“隔離”兩個詞上。職責要求按類的功能單一性定義接口、類、方法。隔離要求最細化的定義接口,盡量避免大而全的接口,不然接口一改,所以的實現類一片報紅,請回答我,有沒有?
實踐:情愿讓類實現多個單一接口,也不要實現一個大而全的接口。
2.5、迪米特原則
官方解釋:迪米特法則(Law of Demeter)又叫作最少知識原則(Least Knowledge Principle 簡寫LKP),一個類對于其他類知道的越少越好,就是說一個對象應當對其他對象有盡可能少的了解,只和朋友通信,不和陌生人說話。?
大白話:電視劇經常有這個臺詞:“你知道的太多了”,然后一槍被崩了。這個原則也是這個道理,就是一個類盡量減少和其他類組合,這樣能減少類之間的耦合,如果實現要關聯,可以通過第三者(朋友)。門面模式和中介者模式就是這個原則的體現。
實踐:一個類里,盡量減少與太多的類接觸,如果不可避免,可以用一個中介類代替。
2.6、開閉原則
官方解釋:在面向對象編程領域中,開閉原則規定“軟件中的對象(類,模塊,函數等等)應該對于擴展是開放的,但是對于修改是封閉的”,這意味著一個實體是允許在不改變它的源代碼的前提下變更它的行為。該特性在產品化的環境中是特別有價值的,在這種環境中,改變源代碼需要代碼審查,單元測試以及諸如此類的用以確保產品使用質量的過程。遵循這種原則的代碼在擴展時并不發生改變,因此無需上述的過程。
大白話:其實這個原則實操性不強。如果這個原則是思想的話,上面的五個原則就是這個思想的實踐,如果我們代碼真的能做到職責單一、面向抽象編程且清爽的編碼規范,則會很容易實現開閉原則。但現實情況是,我們的代碼模塊與模塊,類與類高度耦合、很難見到接口或抽象類、甚至全是面向過程編碼。所以在實際業務中,我們改一點代碼,都要涉及很多項目、類、方法。改完很自信地說:我改動不大,不會影響線上業務的,不用測試,直接上線。請回答我,有沒有?
實踐:盡量避免修改原有的代碼(一點不改也不太可能),前期編碼的時候留好擴展點,使用繼承增加新子類的方式修改原有業務。
三、總結
在學習設計原則和模式的時候,請先保證自己寫出的代碼是整潔且符合規范的,不然再好的原則和模式也拯救不了你那一大坨一大坨的類和方法,最起碼的讀你寫的某一個方法的時候,不用拖滾動條。
真正的理解你所在公司的業務和需求,先在產品和文檔級別上簡化業務、理清編碼的思路,這樣更準確地找出業務的變化點和擴展點。
設計模式其實就是封裝變化,所以編碼的時候多用心變化點,有變化了就考慮抽象出接口或抽象類。當然,在實際業務中,多是增刪改查,如果一昧的到處定義接口,也會增加復雜度,這些可以自己權衡或按團隊的編碼規范實施。
不要刻意地去背這些模式或者照著敲幾遍,沒用的!實際業務編碼中,發現業務變化或擴展點后再去找適合的模式。
總結
以上是生活随笔為你收集整理的面试官:你不懂六大设计原则,回去等通知吧!的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用Jexus 容器化您的 Blazor
- 下一篇: Asp.Net Core Filter