design
最近在項(xiàng)目開發(fā)過程中碰到了一些問題,發(fā)現(xiàn)在每波迭代開發(fā)過程中,經(jīng)常需要去修改之前的代碼,雖然出現(xiàn)這樣的情形很正常,新的需求必然會帶來新的功能新的設(shè)計(jì),導(dǎo)致之前的代碼受到影響。記得看過一個(gè)笑話:
其實(shí)需求設(shè)計(jì)是一個(gè)方面,另外我們作為設(shè)計(jì)開發(fā)人員有時(shí)候也需要去反省,反省一下代碼的設(shè)計(jì)是否合理,為什么新功能的在原有代碼上擴(kuò)展會那么難,為什么我們的代碼這么不穩(wěn)定,牽一發(fā)而動全身? “比設(shè)計(jì)模式更重要的是設(shè)計(jì)原則”面相對象設(shè)計(jì)的概念大家也都知道,它的設(shè)計(jì)目標(biāo)就是希望軟件系統(tǒng)能做到以下幾點(diǎn):
這幾個(gè)可以用來檢測我們的軟件系統(tǒng)是不是設(shè)計(jì)得合理,而如何設(shè)計(jì)出易于維護(hù)和擴(kuò)展的軟件系統(tǒng)是有設(shè)計(jì)原則可以遵循指導(dǎo)的,Robert C. Martin提出了面相對象設(shè)計(jì)的五個(gè)基本原則(SOLID):
我們在進(jìn)行面相對象設(shè)計(jì)的時(shí)候應(yīng)該牢記這幾個(gè)原則,這能讓你成為更優(yōu)秀的設(shè)計(jì)開發(fā)人員---至少你的代碼不會那么爛,下面來簡單了解一下這幾個(gè)原則。 單一職責(zé)原則:Single Responsibility Principle
簡單來說一個(gè)類只做好一件事就行,不去管跟自己不相干的,狗拿耗子多管閑事,其核心就是解耦以及高內(nèi)聚。這個(gè)原則看著很簡單,我們在寫代碼的時(shí)候即便不知道這個(gè)原則也會往這個(gè)方向靠攏,寫出功能相對單一的類,不過這個(gè)原則很容易違背,因?yàn)榭赡苡捎谀撤N原因,原來功能單一的類需要被細(xì)化成顆粒更小的職責(zé)1跟職責(zé)2,所以在每次迭代過程中可能需要重新梳理重構(gòu)之前編寫的代碼,將不同的職責(zé)封裝到不同的類或者模塊中。 @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)上傳跟下載:
這就違反了單一職責(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ì)最基本的原則。 舉個(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)思想就是:
不過在軟件開發(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)系是否合理。 鯨魚繼承自魚類 然后在水里的時(shí)候,魚能夠進(jìn)行呼吸: if(isInwater){ //在水中了,開始呼吸 fish.breath();} 當(dāng)我們把鯨魚這個(gè)子對象替換原來的基類魚對象,鯨魚在水里開始呼吸,這時(shí)問題就出現(xiàn)了,鯨魚是哺乳動物,在水里呼吸是沒法呼吸的,一直在水里就GG思密達(dá)了,所以這違反了該原則,我們就可以判斷鯨魚繼承于魚類不合理,需要去重新設(shè)計(jì)。 接口隔離原則:Interface Segregation Principle該原則的定義:不能強(qiáng)迫用戶去依賴那些他們不使用的接口。簡單來說就是客戶端需要什么接口,就提供給它什么樣的接口,其它多余的接口就不要提供,不要讓接口變得臃腫,否則當(dāng)對象一個(gè)沒有使用的方法被改變了,這個(gè)對象也將會受到影響。接口的設(shè)計(jì)應(yīng)該遵循最小接口原則,其實(shí)這也是高內(nèi)聚的一種表現(xiàn),換句話說,使用多個(gè)功能單一、高內(nèi)聚的接口總比使用一個(gè)龐大的接口要好。 不滿足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)來編程。 針對接口編程 @protocol MessageDelegate 當(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ò)展性較高的代碼有幫助。
|
|