我對SOLID的理解
超前的設計或者過度的設計都不是良好的設計,很多時候我們等到程式碼在第一次變化的時候可以及時作出反應就夠了
單一責任原則(The Single Responsibility Principle )
根據實際情況,拿捏需求的拆分力度,根據拆分的塊,來設計對應的類
單一責任原則:我們設計一個類的時候,應該儘量把類的職責單一化;那麼我們拿到需求的時候,應該對需求進行分析,再拆分職責,再設計對應的類。在實際工作中吧,其實大部分的程式猿都知道這個道理,主要的阻力是開發時間(偷懶),為了方便處理就不搞太多類了;還是有個原因,就是如果拆分力度大,拆分的太細,那麼可能會出現一個簡單的需求,你寫了好幾個類,這樣也不是可取的,這種有點過度設計了,沒有必要。
舉個例子吧,我是做iOS的,就說說介面相關的需求吧,比如說現在產品的需求是做一個word功能的屬性面板,裡面有幾個模組:選擇字型,設定對齊方式,設定字型顏色,每個模組都是列表(tableView),那麼在開發前,就應該拆分好,比如說屬性面板(容器類),三個子模組(對應三個類),這樣來開發,這就是單一責任的應用了
再舉個例子,比如這時候要設計一個工廠類,工廠類需要生產A,B產品,還有一個原料的預加工,現在的需求是原料的預加工,A和B都是一樣的,也就是說A和B可以共用這個方法,那麼這三個功能就都堆在工廠類裡面了。這樣是違背了單一責任原則了,但是如果需求確實這邊簡單,比較小需求,那麼這樣來開發我覺得也沒有問題,畢竟對於大部分懟業務的程式猿來說,又快又穩的出貨才是王道。
如果第二期需求來了,要求改動A產品的預加工方式,那麼這時候,A和B就不可以公用一個預加工方法了,那麼這個時候,就是要及時作出重新設計的考慮了,而且這時候的考慮可以多考慮一下未來可能的改動。
說回剛來產品要求改A產品的預加工需求,如果我們改了工廠類的預加工方法,那麼B產品就受影響了。這個就說明了單一責任原則的重要性了,可能降低以後版本迭代中,改動的影響面。對於這個例子,可能一些同學覺得這個很簡單就知道我改動預加工方法會影響到B產品,問題是,在比較大專案,複雜的專案中,有時候改動了一個地方,影響到另一個地方是很難發現的,測試階段發現還好,有時候可能不是很明顯,也不是嚴重的影響,有可能上線了好久才偶然間發現
除了類需要遵循單一職責原則,方法也同樣需要,一般方法內的程式碼不能太多
開閉原則(The Open Closed Principle)
用抽象構建框架,用實現擴充套件細節
開閉原則:就是寫的程式碼即要有開放性,也要有封閉性,比如現在寫一個加法需求,如果一開始就寫一個加法類,那麼後期擴充套件,有減法,乘法,除法等,那麼是不是繼續建立新的類,那麼這些加減乘除是不是應該會存在共用的東西,那麼就可以抽成一個計算類,這個計算類可以做一些共用的約束,比如計算類可以有formula
公式方法,result
計算結果的方法,然後加減乘除繼承於計算類,重寫對應的方法即可。
那麼這樣的操作,就是對計算類封閉,但是計算類又對外開放,比如繼承他,去實現一些細節問題
所以開閉原則說的就是這個意思,用抽象構建框架,用實現擴充套件細節,比如抽象類(計算類),實現細節由加減乘除子類去做。
需要說明的是,對修改關閉不是說軟體設計不能做修改,只是儘量不要做不必要的修改。怎麼才能做到呢?那就是有相應的擴充套件性。
其實,軟體有相應的擴充套件性是好處,但是不能說每個地方都有擴充套件。反而造成了程式碼的臃腫。所以這裡的擴充套件與修改關閉是有限制的。這個結合實際的工作開發,如果我們遇到的需求,都考慮弄個抽象類,都自己考慮了以後的一堆擴充套件,不是說這個考慮不好,只是在開發上浪費了時間,有可能你的架構,在未來很長時間都沒有用上,那麼這就形同臃腫的架構設計,所以實際開發,還是要按照實際情況去處理,不要為了達到開閉原則寫開閉原則。
還有抽象層儘量保持穩定,儘量不修改,因為我們在開發中,修改老舊程式碼,評估最多的是影響面,如果動到了抽象層,意味著影響面很大
里氏替換原則(Liskov Substitution Principle)
規範子類的書寫規則,實現父類抽象方法,不能覆蓋父類的具體方法,以此達到父類的方法不被覆蓋和父類可以出現的地方,子類就能出現(意思就是比如一個方法的引數是傳父類型別,那麼這時候傳子類進去,也得是沒有問題的)
我們設計基類的時候,應該儘量做到基類是抽象的,儘量抽象,如果一個基類的功能夠完善,那麼這個應該定義為具體類,而不是基類,因為越完善越具體的類,以後子類繼承他,子類的擴充套件性就越差。這就違背了開閉原則。
至於里氏替換原則,說的就是規範一些子類寫法的規則,比如
- 子類可以實現父類的抽象方法,但是不能覆蓋父類的具體方法
- 子類可以增加自己特有的方法
- 當子類覆蓋或實現父類的方法時,方法的前置條件(即方法的形參)要比父類方法的輸入引數更寬鬆。
- 當子類的方法實現父類的抽象方法時,方法的後置條件(即方法的返回值)要比父類更嚴格。
第三和四點不太理解,這有一個詳細的問題,但是我沒看懂(https://blog.csdn.net/qq_3496...
說簡單點就是開閉原則就是父類儘量抽象,子類擴充套件,里氏替換原則就是子類實現的細節怎麼來寫,怎麼規範,還有繼承必須確保父類所擁有的性質在子類中仍然成立。
介面隔離原則(Interface Segregation Principle)
介面儘量細化,不要臃腫,這裡介面的意思就是oc的代理,java的interface
比如現在介面1有5個方法,類A依賴介面1的1-3個方法,類B依賴介面1的3-5個方法;那麼類A依賴介面1就必須實現這5個方法,但是4、5方法對於類A來說是完全沒有必要實現的,所以這樣的介面1設計就是臃腫的,所以我們應該細化他,變成兩個介面,對應類A和B
在oc中代理方法有可選的,那麼似乎就沒有這個問題了,這麼看也確實沒有,但是@protocol
設定@optional
的意思是,可以實現,也可以不實現,是非必須的意思。介面隔離原則就是完全不必要實現的,這種情況就需要考慮拆分,細化介面
依賴倒置原則(Dependency InversionPrinciple)
面向抽象(介面)程式設計
介面,抽象,意思就是定義一種規範,協議,自己不實現具體的程式碼,只是指明瞭大方向的意思,比如一個公司的老闆就是介面,就是抽象的,因為他不用做細節的東西,但是他指定的方向,規範
細節就是具體的實現
比如在iOS中,協議就是介面,抽象類的抽象方法也是介面,我們常說要面向介面程式設計,而不是面向實現程式設計,因為介面是文件的,細節的實現是多變的。我們的程式設計是追求穩定維護程式的,所以我們要面向介面程式設計
舉個例子
初級程式猿類primary
方法:我的工資是1萬
薪酬管理類
方法:claculate:(primary *)pri
那麼薪酬管理類可以計算出初級程式猿的工資了,但是有個問題,如果要計算中級程式猿的話,那薪酬管理類不就得再新增方法,因為claculate:(primary *)pri
方法的入參是初級程式猿類,那這樣不行,以後越來越多崗位不就得總去修改薪酬管理類。
所以這時候,我們要面向介面程式設計
定義一個協議
@protocol EmployeeDelegate <NSObject> - (void)calculateSalary; @end
薪酬管理類
- (void)claculate:(id<EmployeeDelegate>)pri;
這樣每個崗位都實現代理方法,然後薪酬管理類的入參是實現了對應代理方法的類,這樣就不用新增崗位,就去改管理類的程式碼了,這就是面向介面程式設計的一個例子了
總結
單一責任原則是最基本的程式設計要求,一般我們主要一個類一個責任,一個方法一個責任這樣寫編寫程式碼。關於程式碼整潔的話,我的理解就是做到抽取程式碼,方法內容簡潔,對應的方法內做對應的事,這樣可以方便以後的查詢,維護。
開閉原則就是說要用抽象搭建框架,實現擴充套件細節,細節交給具體類或者子類來處理和擴充套件
里氏替換原則就是規範子類的規範;
介面隔離原則,介面(這裡的介面指oc的協議)細化,不要臃腫;
依賴倒置原則,就是面向介面程式設計
我對SOLID之間關係的理解:單一責任原則是最基本的程式設計原則;開閉原則是整個程式架構的最終目標,里氏替換原則、介面隔離原則,依賴倒置原則都是為了實現開閉原則