一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

談?wù)劽嫦驅(qū)ο笤O(shè)計(jì)(OOD)原則

 尤尤_尤尤 2017-12-26
design

擁有一把錘子未必能成為建筑師

最近在項(xiàng)目開發(fā)過程中碰到了一些問題,發(fā)現(xiàn)在每波迭代開發(fā)過程中,經(jīng)常需要去修改之前的代碼,雖然出現(xiàn)這樣的情形很正常,新的需求必然會帶來新的功能新的設(shè)計(jì),導(dǎo)致之前的代碼受到影響。記得看過一個(gè)笑話:

“殺一個(gè)程序員不需要用槍,改三次需求就可以了”

其實(shí)需求設(shè)計(jì)是一個(gè)方面,另外我們作為設(shè)計(jì)開發(fā)人員有時(shí)候也需要去反省,反省一下代碼的設(shè)計(jì)是否合理,為什么新功能的在原有代碼上擴(kuò)展會那么難,為什么我們的代碼這么不穩(wěn)定,牽一發(fā)而動全身?
??我覺得能成為一名程序員,至少不會是一個(gè)笨的人,要完成一個(gè)功能,總能想辦法實(shí)現(xiàn)(不然早被開除啦~),但實(shí)現(xiàn)的方法思路卻有好有壞,不過我認(rèn)為思路可以被引導(dǎo),軟件開發(fā)不是才剛開始,它已經(jīng)存在一段時(shí)間,我們可以吸收前人的一些經(jīng)驗(yàn)教訓(xùn)來提高自己,比如GOF的《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》,幫我們總結(jié)了很多問題的解決思路。這段時(shí)間也花了點(diǎn)時(shí)間學(xué)習(xí)面向?qū)ο笤O(shè)計(jì)的一些思想,也談?wù)勛约旱囊恍├斫狻?br>??提到設(shè)計(jì)模式,我想很多人都看過這塊的一些書籍,不過不知道會不會有跟我一樣的困惑:看的時(shí)候都理解,但是實(shí)際開發(fā)的時(shí)候卻無法融入,后面慢慢就忘記了。尷尬,可能我們只是看到了某個(gè)模式的表面,而隱藏在模式后面的一些“真理”卻沒有去挖掘,這個(gè)模式是要解決什么問題?其實(shí)模式設(shè)計(jì)的背后都是為了遵循某種設(shè)計(jì)原則。

“比設(shè)計(jì)模式更重要的是設(shè)計(jì)原則”

面相對象設(shè)計(jì)的概念大家也都知道,它的設(shè)計(jì)目標(biāo)就是希望軟件系統(tǒng)能做到以下幾點(diǎn):

  • 可擴(kuò)展:新特性能夠很容易的添加到現(xiàn)有系統(tǒng)中,不會影響原本的東西
  • 可修改:當(dāng)修改某一部分的代碼時(shí),不會影響到其它不相關(guān)的部分
  • 可替代:將系統(tǒng)中某部分的代碼用其它有相同接口的類替換時(shí),不會影響到現(xiàn)有系統(tǒng)

這幾個(gè)可以用來檢測我們的軟件系統(tǒng)是不是設(shè)計(jì)得合理,而如何設(shè)計(jì)出易于維護(hù)和擴(kuò)展的軟件系統(tǒng)是有設(shè)計(jì)原則可以遵循指導(dǎo)的,Robert C. Martin提出了面相對象設(shè)計(jì)的五個(gè)基本原則(SOLID):

  • S-單一職責(zé)原則
  • O-開放關(guān)閉原則
  • L-里氏替換原則
  • I-接口隔離原則
  • D-依賴倒置原則

我們在進(jìn)行面相對象設(shè)計(jì)的時(shí)候應(yīng)該牢記這幾個(gè)原則,這能讓你成為更優(yōu)秀的設(shè)計(jì)開發(fā)人員---至少你的代碼不會那么爛,下面來簡單了解一下這幾個(gè)原則。

單一職責(zé)原則:Single Responsibility Principle

一個(gè)類有且僅有一個(gè)職責(zé),只有一個(gè)引起它變化的原因。

簡單來說一個(gè)類只做好一件事就行,不去管跟自己不相干的,狗拿耗子多管閑事,其核心就是解耦以及高內(nèi)聚。這個(gè)原則看著很簡單,我們在寫代碼的時(shí)候即便不知道這個(gè)原則也會往這個(gè)方向靠攏,寫出功能相對單一的類,不過這個(gè)原則很容易違背,因?yàn)榭赡苡捎谀撤N原因,原來功能單一的類需要被細(xì)化成顆粒更小的職責(zé)1跟職責(zé)2,所以在每次迭代過程中可能需要重新梳理重構(gòu)之前編寫的代碼,將不同的職責(zé)封裝到不同的類或者模塊中。
舉個(gè)栗子:

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

DataTransfer包含上傳跟下載功能,仔細(xì)考慮可以發(fā)現(xiàn)這相當(dāng)于實(shí)現(xiàn)了兩個(gè)功能,一個(gè)負(fù)責(zé)上傳的相關(guān)邏輯,另一個(gè)負(fù)責(zé)下載的邏輯,而這個(gè)兩個(gè)功能相對對立,當(dāng)有一個(gè)功能改變的時(shí)候,比如我們之前是使用AFNetworking,現(xiàn)在想換成其它第三方或者nsurlconnection來實(shí)現(xiàn)上傳跟下載:

  • 上傳方式變更,導(dǎo)致DataTransfer變更
  • 下載方式變更,導(dǎo)致 DataTransfer變更

這就違反了單一職責(zé)的原則,所以需要將不同的功能拆解成兩個(gè)不同的類,來負(fù)責(zé)各自的職責(zé),不過這個(gè)拆的粒度可能因人而已,有時(shí)候并不需要拆的過細(xì),不要成了為設(shè)計(jì)而設(shè)計(jì)。

單一職責(zé)

??在我們項(xiàng)目中經(jīng)常看到很多違反這條原則的代碼,而且違反的比較明顯,許多類都是豐富功能的超級集合,整個(gè)類變得臃腫難以理解,這時(shí)候就需要我們有意識地去重構(gòu)了。

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

開閉原則的定義是說一個(gè)軟件實(shí)體如類,模塊和函數(shù)應(yīng)該對擴(kuò)展開放,而對修改關(guān)閉,具體來說就是你應(yīng)該通過擴(kuò)展來實(shí)現(xiàn)變化,而不是通過修改原有的代碼來實(shí)現(xiàn)變化,該原則是面相對象設(shè)計(jì)最基本的原則。
??之前說過在項(xiàng)目中每當(dāng)需求需改的時(shí)候經(jīng)常需要對代碼有很大的改動,很大程度上就是因?yàn)槲覀儗@個(gè)原則理解的不夠透徹。
??開閉原則的關(guān)鍵在于抽象,我們需要抽象出那些不會變化或者基本不變的東西,這部分東西相對穩(wěn)定,這也就是對修改關(guān)閉的地方(這并不意味著不可以再修改),而對于那些容易變化的部分我們也對其封裝,但是這部分是可以動態(tài)修改的,這也就是對擴(kuò)展開發(fā)的地方,比如設(shè)計(jì)模式中的策略模式和模板模式就是在實(shí)現(xiàn)這個(gè)原則(現(xiàn)在應(yīng)該對模式有更感性的認(rèn)識了吧~)。

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

開閉原則

實(shí)現(xiàn)開閉原則的指導(dǎo)思想就是:

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

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

里氏替代原則:Liskov Substitution Principle

該原則的定義:所有引用基類的地方必須能透明地使用其子類的對象。簡單來說,所有使用基類代碼的地方,如果換成子類對象的時(shí)候還能夠正常運(yùn)行,則滿足這個(gè)原則,否則就是繼承關(guān)系有問題,應(yīng)該廢除兩者的繼承關(guān)系,這個(gè)原則可以用來判斷我們的對象繼承關(guān)系是否合理。
比如有一個(gè)鯨魚的類,我們讓鯨魚繼承于魚類,然后魚類有個(gè)呼吸的功能:

鯨魚繼承自魚類

然后在水里的時(shí)候,魚能夠進(jìn)行呼吸:

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

當(dāng)我們把鯨魚這個(gè)子對象替換原來的基類魚對象,鯨魚在水里開始呼吸,這時(shí)問題就出現(xiàn)了,鯨魚是哺乳動物,在水里呼吸是沒法呼吸的,一直在水里就GG思密達(dá)了,所以這違反了該原則,我們就可以判斷鯨魚繼承于魚類不合理,需要去重新設(shè)計(jì)。
??通常在設(shè)計(jì)的時(shí)候,我們都會優(yōu)先采用組合而不是繼承,因?yàn)槔^承雖然減少了代碼,提高了代碼的重用性,但是父類跟子類會有很強(qiáng)的耦合性,破壞了封裝。

接口隔離原則:Interface Segregation Principle

該原則的定義:不能強(qiáng)迫用戶去依賴那些他們不使用的接口。簡單來說就是客戶端需要什么接口,就提供給它什么樣的接口,其它多余的接口就不要提供,不要讓接口變得臃腫,否則當(dāng)對象一個(gè)沒有使用的方法被改變了,這個(gè)對象也將會受到影響。接口的設(shè)計(jì)應(yīng)該遵循最小接口原則,其實(shí)這也是高內(nèi)聚的一種表現(xiàn),換句話說,使用多個(gè)功能單一、高內(nèi)聚的接口總比使用一個(gè)龐大的接口要好。
??舉個(gè)簡單的例子:比如我們有個(gè)自行車接口,這個(gè)接口包含了很多方法,包括GPS定位,以及換擋的方法

不滿足ISP原則

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

滿足ISP原則

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

@interface ViewController ()

依賴倒置原則:Dependence Inversion Principle

該原則的定義:高層模塊不應(yīng)該依賴低層模塊,兩者都應(yīng)該依賴其抽象;抽象不應(yīng)該依賴細(xì)節(jié);細(xì)節(jié)應(yīng)該依賴抽象。其實(shí)這就是我們經(jīng)常說的“針對接口編程”,這里的接口就是抽象,我們應(yīng)該依賴接口,而不是依賴具體的實(shí)現(xiàn)來編程。
??如你在Sqlite數(shù)據(jù)庫的基礎(chǔ)上開發(fā)一套新的數(shù)據(jù)庫系統(tǒng)AWEDatabase,這時(shí)候Sqlite相當(dāng)于底層模塊,而你的AWEDatabase就屬于高層模塊;而從AWEDatabase開發(fā)使用者來看,他的業(yè)務(wù)層就相當(dāng)于高層模塊,而AWEDatabase就變成底層模塊了,所以模塊的高低應(yīng)該是從開發(fā)者當(dāng)前的角度來看的,不過DIP原則從不同角度來看它都適合且需要被遵守。假如我們高層模塊直接依賴于底層模塊,帶來的后果是每次底層模塊改動,高層模塊就會受到影響,整個(gè)系統(tǒng)就變得不穩(wěn)定,這也違反了開放關(guān)閉原則。
??通常我們會通過引入中間層的方式來解決這個(gè)問題,這個(gè)中間層相當(dāng)于一個(gè)抽象接口層,高層模塊和底層模塊都依賴于這個(gè)中間層來交互,這樣只要中間抽象層保持不變,底層模塊改變不會影響到高層模塊,這就滿足了開放關(guān)閉原則;而且假如高層模塊跟底層模塊同時(shí)處于開發(fā)階段,這樣有了中間抽象層之后,每個(gè)模塊都可以針對這個(gè)抽象層的接口同時(shí)開發(fā),高層模塊就不需要等到底層模塊開發(fā)完畢才能繼續(xù)了。
??比如在我們項(xiàng)目中有涉及IM的功能,現(xiàn)在這個(gè)IM模塊采用的是XMPP協(xié)議來實(shí)現(xiàn),客戶端通過這個(gè)模塊來實(shí)現(xiàn)消息的收發(fā),但是假如后面我們想要換成其它協(xié)議,比如MQTT等,針對接口編程的話就可以讓我們很輕松的實(shí)現(xiàn)模塊替換:

針對接口編程
@protocol MessageDelegate @required-(void)goOnline;-(void)sendMessage:(NSString*)content;@end//xmpp實(shí)現(xiàn)@interface XMPPMessageCenter @end//MQTT實(shí)現(xiàn)@interface MQTTMessageCenter @end//業(yè)務(wù)層@interface BussinessLayer//使用遵循MessageDelegate協(xié)議的對象,針對接口編程,以后替換也很方便@property(nonatomic,strong)id messageCenter;@end

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

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

    本站是提供個(gè)人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    日韩人妻免费视频一专区| 国产又粗又猛又大爽又黄| 亚洲中文字幕高清视频在线观看| 日本视频在线观看不卡| 中文字幕中文字幕在线十八区| 精品熟女少妇一区二区三区| 日本不卡在线视频你懂的| 国产传媒中文字幕东京热| 男人的天堂的视频东京热| 国产精品亚洲一级av第二区| 欧美六区视频在线观看| 国产精品99一区二区三区| 久久精品国产一区久久久| 视频一区二区 国产精品| 国产精品国产亚洲看不卡| 欧美熟妇一区二区在线| 国产不卡视频一区在线| 欧美日韩国产黑人一区| 欧美极品欧美精品欧美| 日韩一区二区三区嘿嘿| 中文字幕一区二区免费| 九九热精品视频在线观看| 亚洲视频在线观看免费中文字幕 | 久久精品色妇熟妇丰满人妻91| 国产一级性生活录像片| 日韩欧美中文字幕av| 91欧美激情在线视频| 欧美日韩无卡一区二区| 国产午夜精品福利免费不| 91亚洲熟女少妇在线观看| 成人精品一级特黄大片| 狠色婷婷久久一区二区三区| 福利一区二区视频在线| 欧美成人国产精品高清| 91日韩在线观看你懂的| 日韩一区二区三区在线日| 少妇一区二区三区精品| 亚洲国产成人爱av在线播放下载 | 欧美激情一区二区亚洲专区| 又大又长又粗又黄国产| 日韩中文高清在线专区|