【code_hyy_基礎(chǔ)】面向?qū)ο笤O(shè)計原則

單一職責原則,開閉原則襟齿,依賴倒置原則(面向接口編程)猜欺,里氏替換原則开皿,接口隔離原則赋荆。

面相對象設(shè)計的概念大家也都知道窄潭,它的設(shè)計目標就是希望軟件系統(tǒng)能做到以下幾點:
  • 可擴展:新特性能夠很容易的添加到現(xiàn)有系統(tǒng)中嫉你,不會影響原本的東西
  • 可修改:當修改某一部分的代碼時,不會影響到其它不相關(guān)的部分
  • 可替代:將系統(tǒng)中某部分的代碼用其它有相同接口的類替換時嚷辅,不會影響到現(xiàn)有系統(tǒng)
這幾個可以用來檢測我們的軟件系統(tǒng)是不是設(shè)計得合理潦蝇,而如何設(shè)計出易于維護和擴展的軟件系統(tǒng)是有設(shè)計原則可以遵循指導的,Robert C. Martin提出了面相對象設(shè)計的五個基本原則(SOLID):
  • S-單一職責原則
  • O-開放關(guān)閉原則
  • L-里氏替換原則
  • I-接口隔離原則
  • D-依賴倒置原則

單一職責原則:Single Responsibility Principle

一個類有且僅有一個職責攘乒,只有一個引起它變化的原因则酝。

簡單來說一個類只做好一件事就行沽讹,不去管跟自己不相干的武鲁,狗拿耗子多管閑事沐鼠,其核心就是解耦以及高內(nèi)聚饲梭。這個原則看著很簡單,我們在寫代碼的時候即便不知道這個原則也會往這個方向靠攏订框,寫出功能相對單一的類穿扳,不過這個原則很容易違背矛物,因為可能由于某種原因,原來功能單一的類需要被細化成顆粒更小的職責1跟職責2特漩,所以在每次迭代過程中可能需要重新梳理重構(gòu)之前編寫的代碼,將不同的職責封裝到不同的類或者模塊中雄卷。
舉個栗子:

@interface DataTransfer : NSObject
-(void)upload:(NSData *)data; //上傳數(shù)據(jù)
-(void)download(NSString*)url;  //根據(jù)URL下載東西
@end

DataTransfer包含上傳跟下載功能丁鹉,仔細考慮可以發(fā)現(xiàn)這相當于實現(xiàn)了兩個功能揣钦,一個負責上傳的相關(guān)邏輯冯凹,另一個負責下載的邏輯宇姚,而這個兩個功能相對對立浑劳,當有一個功能改變的時候魔熏,比如我們之前是使用AFNetworking啼止,現(xiàn)在想換成其它第三方或者nsurlconnection來實現(xiàn)上傳跟下載:

  • 上傳方式變更滓窍,導致DataTransfer變更
  • 下載方式變更巩那,導致 DataTransfer變更

這就違反了單一職責的原則即横,所以需要將不同的功能拆解成兩個不同的類东囚,來負責各自的職責,不過這個拆的粒度可能因人而已桨嫁,有時候并不需要拆的過細璃吧,不要成了為設(shè)計而設(shè)計畜挨。


屏幕快照 2018-10-27 下午7.58.38.png

??在我們項目中經(jīng)痴庇剑看到很多違反這條原則的代碼血当,而且違反的比較明顯臊旭,許多類都是豐富功能的超級集合箩退,整個類變得臃腫難以理解滋戳,這時候就需要我們有意識地去重構(gòu)了奸鸯。

開放關(guān)閉原則:Open Closed Principle

開閉原則的定義是說一個軟件實體如類娄涩,模塊和函數(shù)應該對擴展開放蓄拣,而對修改關(guān)閉球恤,具體來說就是你應該通過擴展來實現(xiàn)變化咽斧,而不是通過修改原有的代碼來實現(xiàn)變化张惹,該原則是面相對象設(shè)計最基本的原則诵叁。

之前說過在項目中每當需求需改的時候經(jīng)常需要對代碼有很大的改動拧额,很大程度上就是因為我們對這個原則理解的不夠透徹侥锦。
??開閉原則的關(guān)鍵在于抽象恭垦,我們需要抽象出那些不會變化或者基本不變的東西,這部分東西相對穩(wěn)定玄柏,這也就是對修改關(guān)閉的地方(這并不意味著不可以再修改)粪摘,而對于那些容易變化的部分我們也對其封裝徘意,但是這部分是可以動態(tài)修改的椎咧,這也就是對擴展開發(fā)的地方邑退,比如設(shè)計模式中的策略模式和模板模式就是在實現(xiàn)這個原則(現(xiàn)在應該對模式有更感性的認識了吧~)地技。

舉個例子:我們需要保存對象到數(shù)據(jù)庫當中,其中有個類似save()的保存方法作谚,這部分應該是不變的雀监,接口相對穩(wěn)定,而具體保存的實現(xiàn)卻有可能不同眨唬,我們現(xiàn)在可能是保存在Sqlite數(shù)據(jù)庫中会前,假如以后如果想保存到一個自己實現(xiàn)的數(shù)據(jù)庫中時,我們只需要實現(xiàn)一個擁有同樣接口的擴展類添加進去即可匾竿,這就是對擴展開放瓦宜,不會對之前的代碼造成任何影響,就可以實現(xiàn)保存到新數(shù)據(jù)庫的功能岭妖,保證了系統(tǒng)的穩(wěn)定性临庇。


屏幕快照 2018-10-27 下午8.04.25.png

實現(xiàn)開閉原則的指導思想就是:

  • 抽象出相對穩(wěn)定的接口,這部分應該不改動或者很少改動
  • 封裝變化

不過在軟件開發(fā)過程中昵慌,要一開始就完全按照開閉原則來可能比較困難假夺,更多的情況是在不斷的迭代重構(gòu)過程中去改進悼尾,在可預見的變化范圍內(nèi)去做設(shè)計俯画。

里氏替代原則:Liskov Substitution Principle

該原則的定義:所有引用基類的地方必須能透明地使用其子類的對象猜憎。簡單來說爬泥,所有使用基類代碼的地方境输,如果換成子類對象的時候還能夠正常運行集晚,則滿足這個原則莲绰,否則就是繼承關(guān)系有問題,應該廢除兩者的繼承關(guān)系戳晌,這個原則可以用來判斷我們的對象繼承關(guān)系是否合理豪嚎。

比如有一個鯨魚的類糯耍,我們讓鯨魚繼承于魚類,然后魚類有個呼吸的功能:

屏幕快照 2018-10-27 下午8.09.22.png

然后在水里的時候虐块,魚能夠進行呼吸:

if(isInwater){
    //在水中了儡率,開始呼吸
    fish.breath();
}

當我們把鯨魚這個子對象替換原來的基類魚對象眉孩,鯨魚在水里開始呼吸吟宦,這時問題就出現(xiàn)了蜗侈,鯨魚是哺乳動物夭苗,在水里呼吸是沒法呼吸的,一直在水里就GG思密達了揽思,所以這違反了該原則执俩,我們就可以判斷鯨魚繼承于魚類不合理爹袁,需要去重新設(shè)計守伸。
??通常在設(shè)計的時候,我們都會優(yōu)先采用組合而不是繼承场晶,因為繼承雖然減少了代碼恨樟,提高了代碼的重用性绳泉,但是父類跟子類會有很強的耦合性,破壞了封裝。

接口隔離原則:Interface Segregation Principle

該原則的定義:不能強迫用戶去依賴那些他們不使用的接口。

簡單來說就是客戶端需要什么接口褂策,就提供給它什么樣的接口,其它多余的接口就不要提供溪猿,不要讓接口變得臃腫媳荒,否則當對象一個沒有使用的方法被改變了钳枕,這個對象也將會受到影響。接口的設(shè)計應該遵循最小接口原則酬荞,其實這也是高內(nèi)聚的一種表現(xiàn)缠犀,換句話說,使用多個功能單一盼理、高內(nèi)聚的接口總比使用一個龐大的接口要好谈山。
??舉個簡單的例子:比如我們有個自行車接口,這個接口包含了很多方法宏怔,包括GPS定位奏路,以及換擋的方法

屏幕快照 2018-10-27 下午8.12.08.png

然后我們發(fā)現(xiàn)即便普通的自行車也需要實現(xiàn)GPS定位以及換擋的功能,顯然這違背了接口隔離的原則臊诊。遵循接口最小化的原則鸽粉,我們重新設(shè)計:

屏幕快照 2018-10-27 下午8.14.21.png

這樣一來每個接口的功能相對單一,使用多個專門的接口比使用一個總的接口要好抓艳,假如我們的山地車沒有沒有GPS定位的功能触机,我們不去繼承實現(xiàn)對應的接口即可,在iOS開發(fā)中有很多這樣的例子,比如UITalbleView的代理有兩個不同的接口儡首,UITableViewDataSource專門負責需要顯示的內(nèi)容销斟,UITableViewDelegate專門負責一些view的自定義顯示,然后我們會繼承多個接口椒舵,這就滿足了ISP原則蚂踊。

@interface ViewController () <UITableViewDataSource,UITableViewDelegate,OtherProtocol>

依賴倒置原則:Dependence Inversion Principle

該原則的定義:高層模塊不應該依賴低層模塊,兩者都應該依賴其抽象笔宿;抽象不應該依賴細節(jié)犁钟;細節(jié)應該依賴抽象。

其實這就是我們經(jīng)常說的“針對接口編程”泼橘,這里的接口就是抽象涝动,我們應該依賴接口,而不是依賴具體的實現(xiàn)來編程炬灭。

如你在Sqlite數(shù)據(jù)庫的基礎(chǔ)上開發(fā)一套新的數(shù)據(jù)庫系統(tǒng)AWEDatabase醋粟,這時候Sqlite相當于底層模塊,而你的AWEDatabase就屬于高層模塊重归;而從AWEDatabase開發(fā)使用者來看米愿,他的業(yè)務層就相當于高層模塊,而AWEDatabase就變成底層模塊了鼻吮,所以模塊的高低應該是從開發(fā)者當前的角度來看的育苟,不過DIP原則從不同角度來看它都適合且需要被遵守。假如我們高層模塊直接依賴于底層模塊椎木,帶來的后果是每次底層模塊改動违柏,高層模塊就會受到影響,整個系統(tǒng)就變得不穩(wěn)定香椎,這也違反了開放關(guān)閉原則漱竖。
??通常我們會通過引入中間層的方式來解決這個問題,這個中間層相當于一個抽象接口層畜伐,高層模塊和底層模塊都依賴于這個中間層來交互馍惹,這樣只要中間抽象層保持不變,底層模塊改變不會影響到高層模塊烤礁,這就滿足了開放關(guān)閉原則讼积;而且假如高層模塊跟底層模塊同時處于開發(fā)階段肥照,這樣有了中間抽象層之后脚仔,每個模塊都可以針對這個抽象層的接口同時開發(fā),高層模塊就不需要等到底層模塊開發(fā)完畢才能繼續(xù)了舆绎。
??比如在我們項目中有涉及IM的功能鲤脏,現(xiàn)在這個IM模塊采用的是XMPP協(xié)議來實現(xiàn),客戶端通過這個模塊來實現(xiàn)消息的收發(fā),但是假如后面我們想要換成其它協(xié)議猎醇,比如MQTT等窥突,針對接口編程的話就可以讓我們很輕松的實現(xiàn)模塊替換:

屏幕快照 2018-10-27 下午8.18.18.png
@protocol MessageDelegate <NSObject>
@required
-(void)goOnline;
-(void)sendMessage:(NSString*)content;
@end

//xmpp實現(xiàn)
@interface XMPPMessageCenter <MessageDelegate>
@end

//MQTT實現(xiàn)
@interface MQTTMessageCenter <MessageDelegate>
@end

//業(yè)務層
@interface BussinessLayer
//使用遵循MessageDelegate協(xié)議的對象,針對接口編程硫嘶,以后替換也很方便
@property(nonatomic,strong)id<MessageDelegate> messageCenter;
@end


當我們在進行面向?qū)ο笤O(shè)計的時候應該充分考慮上面這幾個原則阻问,一開始可能設(shè)計并不完美,不過可以在重構(gòu)的過程中不斷完善沦疾。但其實很多人都跳過了設(shè)計這個環(huán)節(jié)称近,拿到一個模塊直接動手編寫代碼,更不用說去思考設(shè)計了哮塞,項目中也有很多這樣的例子刨秆。當然對于簡單的模塊或許不用什么設(shè)計,不過假如模塊相對復雜的話忆畅,能夠在動手寫代碼之前好好設(shè)計思考一下衡未,養(yǎng)成這個習慣,肯定會對編寫出可讀性家凯、穩(wěn)定性以及可擴展性較高的代碼有幫助缓醋。

最關(guān)鍵的軟件開發(fā)工具是受過良好設(shè)計原則訓練的思維。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末绊诲,一起剝皮案震驚了整個濱河市改衩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌驯镊,老刑警劉巖葫督,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異板惑,居然都是意外死亡橄镜,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門冯乘,熙熙樓的掌柜王于貴愁眉苦臉地迎上來洽胶,“玉大人,你說我怎么就攤上這事裆馒℃⒚ィ” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵喷好,是天一觀的道長翔横。 經(jīng)常有香客問我,道長梗搅,這世上最難降的妖魔是什么禾唁? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任效览,我火速辦了婚禮,結(jié)果婚禮上荡短,老公的妹妹穿的比我還像新娘丐枉。我一直安慰自己,他們只是感情好掘托,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布瘦锹。 她就那樣靜靜地躺著,像睡著了一般闪盔。 火紅的嫁衣襯著肌膚如雪沼本。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天锭沟,我揣著相機與錄音抽兆,去河邊找鬼。 笑死族淮,一個胖子當著我的面吹牛辫红,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播祝辣,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼贴妻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了蝙斜?” 一聲冷哼從身側(cè)響起名惩,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎孕荠,沒想到半個月后娩鹉,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡稚伍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年弯予,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片个曙。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡锈嫩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出垦搬,到底是詐尸還是另有隱情呼寸,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布猴贰,位于F島的核電站对雪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏糟趾。R本人自食惡果不足惜慌植,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望义郑。 院中可真熱鬧蝶柿,春花似錦、人聲如沸非驮。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽劫笙。三九已至芙扎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間填大,已是汗流浹背戒洼。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留允华,地道東北人圈浇。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像靴寂,于是被迫代替她去往敵國和親磷蜀。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容