關(guān)于 Android OOP的六大原則——Android源碼設(shè)計(jì)模式解析與實(shí)戰(zhàn)讀書筆記

轉(zhuǎn)載自http://www.reibang.com/p/4dbf64afad6c?winzoom=1

我總在思考如何讓自己寫成更優(yōu)雅的代碼俯萎,如何寫出更易維護(hù)盟庞,更易讀懂的代碼,我覺得很幸運(yùn)搪哪,第一份實(shí)習(xí)工作贞绳,在很大程度上幫助了我膨疏,但后來其實(shí)我發(fā)現(xiàn)我學(xué)到只是一些皮毛,還有更多需要學(xué)習(xí)的宗苍,所以為了寫出更簡潔稼稿,更易他人讀懂的代碼,我開始接觸兩本書讳窟,一本書現(xiàn)在這里寫讀書筆記的這本書让歼,另一本是很著名的代碼整潔之道,那么我們來介紹一下這兩本書挪钓。

Android源碼設(shè)計(jì)模式解析與實(shí)戰(zhàn)這本書主要講了面向?qū)ο?下面統(tǒng)稱OOP)的六大原則是越,23種設(shè)計(jì)模式以及MVC和MVP這兩種應(yīng)用架構(gòu)去看待應(yīng)用開發(fā)。

當(dāng)我們開始了解并遵守OOP的六大原則時(shí)碌上,我們可以寫出更易擴(kuò)展更簡潔的代碼倚评,并了解到為什么這樣做更好浦徊,其實(shí)Java在面向?qū)ο缶幊痰那疤嵯拢覀儜?yīng)該更多的是面向抽象編程天梧,而這里抽象既包括了抽象類也包括了接口盔性。

23種設(shè)計(jì)模式可以讓我們了解到如何更好的更簡單的解決一個(gè)問題,但是有時(shí)候大家卻在濫用設(shè)計(jì)模式呢岗,所以如何平衡使用也是需要學(xué)習(xí)的冕香。

而最后講的MVC和MVP我們也應(yīng)該更多的了解,Android本身就采用了MVC模式后豫,而MVP模式這兩年也被運(yùn)用的比較廣泛悉尾,以至于官方都推出一個(gè)MVP的DEMO,從而統(tǒng)一大家的書寫習(xí)慣和對(duì)MVP的理解挫酿。

代碼整潔之道這本書其實(shí)我還沒開始看构眯,但之前在別的公司讀過一部分,看到一句話早龟,讓我印象深刻惫霸,第一章作者用每個(gè)人角度討論了什么是更好的代碼,讓我印象深刻那句話是這樣的——什么的代碼是最好的葱弟,就是每當(dāng)我覺得代碼可以進(jìn)行修改和完善的時(shí)候壹店,我最后總是回到起點(diǎn)。

我非常認(rèn)同寫代碼就像寫文章這個(gè)觀點(diǎn)芝加,更多時(shí)候我們不是寫給自己看的硅卢,所以可以做到讓其他人一眼就能看懂你在講什么,實(shí)現(xiàn)了什么功能才是我們需要做的藏杖,這就涉及到很多要做好的事情老赤,更好的命名,更簡潔的函數(shù)制市,職責(zé)單一的類抬旺,易擴(kuò)展的設(shè)計(jì)等等。

OOP六大原則

作者在講述的時(shí)候很生動(dòng)祥楣,舉一系列例子开财,使得我們更好地理解它講述出來的六大原則,那我么下面我們就來看看OOP的六大原則到底如何運(yùn)用和他們起到的作用误褪。

1.單一職責(zé)原則

定義:就一類而言责鳍,應(yīng)該僅有一個(gè)引起它變化的原因。

作者給了另一個(gè)概括兽间,一個(gè)類中應(yīng)該是一組相關(guān)性很高的函數(shù)历葛、數(shù)據(jù)的封裝恤溶,也就是說一個(gè)類它只做一件事鸠天,我并不想成為一個(gè)神一樣的類(什么功能都有什么都可以做),而只是我負(fù)責(zé)一件事剥纷,我把它做的很好,所以我們首先要思考的是我寫這個(gè)類他的職責(zé)是什么?這有時(shí)候總是不能清晰的界定,導(dǎo)致了我們寫出類并沒有很好遵守這個(gè)原則因俐。

作者在舉例的時(shí)候講了一個(gè)很實(shí)在的例子,讓一個(gè)新手去實(shí)現(xiàn)一個(gè)ImageLoader,而他很快就寫好了钳踊,交給主管去看拓瞪,主管卻不是很滿意兵钮,因?yàn)樗言趫D片加載的類中同時(shí)做了兩件事情航罗,一個(gè)是加載的圖片柏锄,一個(gè)是對(duì)圖片緩存,而主管看到后表示了不滿,希望他可以把代碼拆分笤成,把各個(gè)功能獨(dú)立出來上祈,所以在修改后他就把圖片的加載和圖片的緩存分成了兩個(gè)不同的類籽腕,讓它們只負(fù)責(zé)一件事揍很。

而這例子很好的解釋了什么是單一職責(zé)原則,一個(gè)多功能的類傅寡,拆分成若干個(gè)單一功能的類,從而降低了代碼耦合。

2.開閉原則

定義:軟件中的對(duì)象(類、模塊、函數(shù)等)應(yīng)該對(duì)擴(kuò)展開放,但是湾盗,對(duì)于修改應(yīng)該是封閉的。

作者講到我們?cè)陂_發(fā)過程中肺孵,需求總是會(huì)有變化匀借,所以代碼也就免不了進(jìn)行相應(yīng)的改變,而這條原則告訴我們盡量不是通過修改原有代碼來實(shí)現(xiàn)新需求而是對(duì)現(xiàn)有功能進(jìn)行擴(kuò)展來應(yīng)對(duì)需求的變化平窘。我們說到擴(kuò)展可能會(huì)想要繼承吓肋,實(shí)現(xiàn),加入新的類瑰艘,理想的情況是只通過這這幾種方式來完成是鬼,但實(shí)際情況可能是這幾方式的同時(shí)也伴隨著對(duì)原有代碼的的修改。

作者在書中還引用了《面向?qū)ο筌浖?gòu)造》作者的話紫新,因?yàn)槭撬岢隽碎_閉原則均蜜,——程序一旦開發(fā)完成,程序中一個(gè)類(原有代碼)的實(shí)現(xiàn)只應(yīng)該因?yàn)殄e(cuò)誤而被修改芒率,新的或者改變的特性應(yīng)該通過新建不同的類實(shí)現(xiàn)囤耳,新建的類可以通過繼承的方式來重用原有的代碼(可以看出提出開閉原則的人提倡實(shí)現(xiàn)繼承)。

作者在上一個(gè)例子的基礎(chǔ)上舉了一個(gè)例子,那個(gè)新手已經(jīng)完成了圖片加載和圖片緩存的拆分充择,但隨著用戶的增多德玫,有些問題也就暴露了出來,它之前只是使用了內(nèi)存緩存椎麦,所以當(dāng)內(nèi)存緩存的內(nèi)容被清理時(shí)宰僧,用戶就只能再次下載圖片,就導(dǎo)致了圖片加載緩慢和消耗用戶流量观挎,此時(shí)應(yīng)該考慮把圖片緩存到本地琴儿,這樣就解決了內(nèi)存緩存被清理的問題,不必再去重新下載圖片键兜。

而他只是添加一個(gè)新的類凤类,然后在圖片緩存類里面加入使用,通過判斷使用哪種方式緩存普气,此時(shí)主管又給出意見谜疤,用戶可以自己決定使用哪種緩存,單獨(dú)使用某一個(gè)或者同時(shí)使用兩種緩存现诀。而其實(shí)最好的緩存策略其實(shí)是同時(shí)使用夷磕,但是優(yōu)先使用內(nèi)存緩存的,如果被清理了再去查看本地緩存是否存在仔沿。

此時(shí)他已經(jīng)寫了三個(gè)關(guān)于內(nèi)存緩存的類了坐桩,內(nèi)存緩存,本地緩存封锉,雙緩存绵跷,代碼如下,主管看到代碼告訴了他不能每次加入新緩存你都去修改原有代碼成福,這樣很容易引入Bug碾局,且會(huì)使得邏輯越來越復(fù)雜,因?yàn)橐ㄟ^條件判斷到底需要使用哪種緩存奴艾,主管給他講解了開閉原則净当,但是作為新手他并不理解,有點(diǎn)云里霧里的蕴潦,不知如何下手像啼,所以主管親自修改了代碼。

// 新手的代碼

public class ImageLoader {

// 內(nèi)存緩存

ImageCache mImageCache = new ImageCache();

// 本地緩存

DiskCache mDiskCache = new DiskCache();

// 雙緩存

DoubleCache mDoubleCache = new DoubleCache();

//? ....省略下面代碼

1

}

// 主管的代碼

// 首先提取出了一個(gè)圖片緩存接口

public interface ImageCache {

void put(String url, Bitmap bitmap);

Bitmap get(String url);

}

// 然后分別實(shí)現(xiàn)了MemoryCache潭苞,DiskCache忽冻,DoubleCache,代碼省略

public class ImageLoader {

// 圖片緩存此疹,并設(shè)置了內(nèi)存緩存為默認(rèn)方式

private ImageCache mImageCache = new MemoryCache();

//? 注入緩存實(shí)現(xiàn)? 利用了向上轉(zhuǎn)型

public void setImageCache(ImageCache imageCache) {

? ? mImageCache = imageCache;

}

//? 省略其他成員變量和方法? ?

1

2

3

4

5

6

}

// 使用方法 只是通過傳入不同實(shí)現(xiàn)就可以切換緩存方式

ImageLoader loader = new ImageLoader();

loader.setImageCache(new MemoryCache());

loader.setImageCache(new DiskCache());

loader.setImageCache(new DoubleCache());

我們看得到通過setImageCache(ImageCache imageCache) 方式注入不同的緩存實(shí)現(xiàn)甚颂,使得ImageLoader代碼變得更簡單蜜猾,健壯,提升高了它的靈活性和可擴(kuò)展性振诬,如果還有還有新的緩存方式蹭睡,只需要去實(shí)現(xiàn)ImageCachej接口就可以使用了。

所以當(dāng)需求發(fā)生變化時(shí)赶么,應(yīng)該盡量通過擴(kuò)展的方式來實(shí)現(xiàn)變化肩豁,而不是通過修改已有代碼來實(shí)現(xiàn),但要做到開閉原則辫呻,首先我們應(yīng)該先寫出更易擴(kuò)展的代碼清钥。

3.里氏替換原則

定義:所有引用的基類的地方都必須能透明的使用其子類的對(duì)象。

作者用了一句很通俗的話講解了這個(gè)原則——只要父類能出現(xiàn)打的地方之類就可以出現(xiàn)放闺。

就像開閉原則中舉的例子祟昭,主管修改了代碼,創(chuàng)建了一個(gè)ImageCache怖侦,而其他緩存類都是他的實(shí)現(xiàn)類篡悟,而setImageCache(ImageCache imageCache) 需要的就是ImageCache類型,這時(shí)候我們就可以使用MemoryCache匾寝,DiskCache搬葬,DoubleCache來替換ImageCache的工作。ImageCache確定了規(guī)范艳悔,而新的緩存需求都可以通過實(shí)現(xiàn)它然后替換ImageCache來工作急凰,從而保證了可擴(kuò)展性。

故里氏替換原則就是通過建立抽象猜年,建立規(guī)范抡锈,然后在運(yùn)行時(shí)通過具體實(shí)現(xiàn)來替換掉抽象,從而保證了系統(tǒng)的擴(kuò)展性和靈活性乔外〈踩可見,在開發(fā)過程中運(yùn)用抽象是走向代碼優(yōu)化的重要一步袁稽。

開閉原則和里氏替換原則往往都是一同出現(xiàn)的勿璃,通過里氏替換原則達(dá)到對(duì)擴(kuò)展的開發(fā)擒抛,對(duì)修改關(guān)閉的效果推汽。

4.依賴倒置原則

定義:指代了一種特定形式的解耦形式,使得高層次的模塊不依賴于低層次的模塊的實(shí)現(xiàn)細(xì)節(jié)的目的歧沪,依賴模塊被顛倒了歹撒。

依賴倒置原則的三個(gè)關(guān)鍵點(diǎn):

(1).高層次模塊不應(yīng)該依賴于底層模塊,兩者都應(yīng)該依賴其抽象诊胞;

(2).抽象不應(yīng)依賴細(xì)節(jié)暖夭;

(3).細(xì)節(jié)應(yīng)該依賴抽象锹杈。

接下來作者解釋了一些概念:抽象就是指接口或者抽象類;細(xì)節(jié)就是實(shí)現(xiàn)類迈着;高層模塊就是調(diào)用端竭望,低層模塊就是具體實(shí)現(xiàn)類。

所以作者說依賴倒置原則在Java中表現(xiàn)就是:模塊間依賴是通過抽象發(fā)生的裕菠,實(shí)現(xiàn)類之間并不產(chǎn)生直接依賴關(guān)系咬清,其依賴關(guān)系是通過接口或抽象類產(chǎn)生的。

一句話概括:面向接口編程奴潘,或者說面向抽象編程旧烧。

我們依然可以通過上面的例子繼續(xù)說明,代碼如下:

// 如果在ImageLoader中直接這樣寫的話

// 就是直接依賴于細(xì)節(jié)(直接依賴實(shí)現(xiàn)類)

private DoubleCache mImageCache = new DoubleCache();

// 而主管的代碼卻直接完成1.2.3.4這是個(gè)原則

// 依賴于抽象画髓,通過向上轉(zhuǎn)型掘剪,有一個(gè)默認(rèn)的實(shí)現(xiàn)類

private ImageCache mImageCache = new MemoryCache();

// 設(shè)置緩存策略,依賴于抽象

public void setImageCache(ImageCache imageCache) {

mImageCache = imageCache;

}

依賴于抽象奈虾,依賴于基類夺谁,這樣當(dāng)需求發(fā)生變化,只需要實(shí)現(xiàn)ImageCache或者繼承已實(shí)現(xiàn)的之類都可以完成緩存功能愚墓,然后將實(shí)現(xiàn)注入到setImageCache(ImageCache imageCache)就可以了予权。

5.接口隔離原則

定義:客戶端不應(yīng)該依賴它不需要的接口±瞬幔或者說類的依賴關(guān)系應(yīng)該將在最小的接口上扫腺。

作者說接口隔離的目的是系統(tǒng)接口耦合,從而容易重構(gòu)村象、更改和重新部署笆环。一句話:讓客戶端以來的接口盡可能小。

作者舉了一個(gè)例子厚者,當(dāng)我們?cè)谑褂昧鞯臅r(shí)候我們需要在finally中判斷是否為空躁劣,如果不為空需要close()它,但每次使用流库菲,都這么寫账忘,也會(huì)讓代碼變得不優(yōu)美,這個(gè)時(shí)候我們考慮借助外力熙宇,就比如Java為我們提供了一個(gè)Closeable接口鳖擒,而它有100多個(gè)實(shí)現(xiàn)類,所以那些類都可以使用它烫止,代碼如下:

// 這就是修改之前的代碼 try/catch中還有try/catch

FileOutputStream fileOutputStream = null;

try {

// 邏輯省略

} catch (Exception e) {

e.printStackTrace();

} finally {

if (fileOutputStream != null) {

try {

fileOutputStream.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

// 寫了個(gè)CloseUtil類蒋荚,然后西面提供這個(gè)靜態(tài)方法,所有實(shí)現(xiàn)了Closeable的類都可以調(diào)用這個(gè)方法

public static void closeQuietly (Closeable closeable) {

if (closeable != null) {

try {

closeable.close();

} catch (IOException e) {

e.printStackTrace();

}

}

}

// 我們只需要在finally中調(diào)用這一句話就好了

CloseUtil.closeQuietly(xxx);

不僅讓代碼的可讀性增加了馆蠕,還保證了它的重用性期升,這里也用到了依賴倒置原則惊奇,closeQuietly()方法的參數(shù)就一個(gè)抽象,做到了我只需要知道這個(gè)對(duì)象是可關(guān)閉的播赁,其他一概不管辛颂郎,也就是作者所說的接口隔離原則。

6.迪米特原則

定義:一個(gè)對(duì)象應(yīng)該對(duì)其他對(duì)象有最少的了解容为。

通俗的講祖秒,一個(gè)類應(yīng)該對(duì)自己需要耦合或者調(diào)用的類知道的最少,類的內(nèi)部如何實(shí)現(xiàn)與調(diào)用者或者依賴者沒有關(guān)系舟奠,只需要知道它需要的方法即可竭缝,其他的一概不管,類與類之間的關(guān)系越密切沼瘫,耦合度也就越大抬纸。

迪米特原則還有一個(gè)英文解釋:Only talk to your immediate friends.翻譯過來也就是說之與直接朋友進(jìn)行通信。

作者舉了例子方便我們理解耿戚,現(xiàn)在有三個(gè)類湿故,一個(gè)是房間類,中介類以及租客類膜蛔,租過房子的朋友都應(yīng)該知道坛猪,中介手里是有很多是房子的,而我們想要找什么樣的房子只要告訴中介條件皂股,他就會(huì)幫你找到合適的房子墅茉,這里也是,在租客類眼中他的直接朋友就是中介類呜呐,我所有的找房子就斤,打掃房間,修電器蘑辑,交水電費(fèi)都找他一個(gè)類就可以洋机,因?yàn)樗麜?huì)幫我們都搞定,至于房東長什么樣子洋魂,房產(chǎn)證放在哪里了租客就都不需要關(guān)心了绷旗,這樣就形成了租客只和中介打交道,而中介管理者房屋列表副砍,這也就是迪米特原則衔肢。

上面就是OOP的六大原則,我講的還是太啰嗦了址晕,希望通過更多的寫作膀懈,可以改善自己的表達(dá)顿锰,也希望可以把自己學(xué)到的知識(shí)通過自己表述講給他講聽谨垃。Android源碼設(shè)計(jì)模式卻是一本好書启搂,希望大家也可以讀一讀。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末刘陶,一起剝皮案震驚了整個(gè)濱河市胳赌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌匙隔,老刑警劉巖疑苫,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異纷责,居然都是意外死亡捍掺,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門再膳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來挺勿,“玉大人,你說我怎么就攤上這事喂柒〔黄浚” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵灾杰,是天一觀的道長蚊丐。 經(jīng)常有香客問我,道長艳吠,這世上最難降的妖魔是什么麦备? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮昭娩,結(jié)果婚禮上泥兰,老公的妹妹穿的比我還像新娘。我一直安慰自己题禀,他們只是感情好鞋诗,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著迈嘹,像睡著了一般削彬。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上秀仲,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天融痛,我揣著相機(jī)與錄音,去河邊找鬼神僵。 笑死雁刷,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的保礼。 我是一名探鬼主播沛励,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼责语,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了目派?” 一聲冷哼從身側(cè)響起坤候,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎企蹭,沒想到半個(gè)月后白筹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谅摄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年徒河,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片送漠。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡虚青,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出螺男,到底是詐尸還是另有隱情棒厘,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布下隧,位于F島的核電站奢人,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏淆院。R本人自食惡果不足惜何乎,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望土辩。 院中可真熱鬧支救,春花似錦、人聲如沸拷淘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽启涯。三九已至贬堵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間结洼,已是汗流浹背黎做。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留松忍,地道東北人蒸殿。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親宏所。 傳聞我的和親對(duì)象是個(gè)殘疾皇子酥艳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353