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

更好的代碼

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

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

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

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

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

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

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

OOP六大原則

作者在講述的時候很生動,舉一系列例子,使得我們更好地理解它講述出來的六大原則,那我么下面我們就來看看OOP的六大原則到底如何運用和他們起到的作用。

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

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

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

作者在舉例的時候講了一個很實在的例子,讓一個新手去實現(xiàn)一個ImageLoader,而他很快就寫好了,交給主管去看,主管卻不是很滿意,因為他把在圖片加載的類中同時做了兩件事情,一個是加載的圖片,一個是對圖片緩存,而主管看到后表示了不滿,希望他可以把代碼拆分,把各個功能獨立出來,所以在修改后他就把圖片的加載和圖片的緩存分成了兩個不同的類,讓它們只負(fù)責(zé)一件事。
而這例子很好的解釋了什么是單一職責(zé)原則,一個多功能的類,拆分成若干個單一功能的類,從而降低了代碼耦合。

2.開閉原則

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

作者講到我們在開發(fā)過程中,需求總是會有變化,所以代碼也就免不了進行相應(yīng)的改變,而這條原則告訴我們盡量不是通過修改原有代碼來實現(xiàn)新需求而是對現(xiàn)有功能進行擴展來應(yīng)對需求的變化。我們說到擴展可能會想要繼承,實現(xiàn),加入新的類,理想的情況是只通過這這幾種方式來完成,但實際情況可能是這幾方式的同時也伴隨著對原有代碼的的修改。

作者在書中還引用了《面向?qū)ο筌浖?gòu)造》作者的話,因為是他提出了開閉原則,——程序一旦開發(fā)完成,程序中一個類(原有代碼)的實現(xiàn)只應(yīng)該因為錯誤而被修改,新的或者改變的特性應(yīng)該通過新建不同的類實現(xiàn),新建的類可以通過繼承的方式來重用原有的代碼(可以看出提出開閉原則的人提倡實現(xiàn)繼承)。

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

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

此時他已經(jīng)寫了三個關(guān)于內(nèi)存緩存的類了,內(nèi)存緩存,本地緩存,雙緩存,代碼如下,主管看到代碼告訴了他不能每次加入新緩存你都去修改原有代碼,這樣很容易引入Bug,且會使得邏輯越來越復(fù)雜,因為要通過條件判斷到底需要使用哪種緩存,主管給他講解了開閉原則,但是作為新手他并不理解,有點云里霧里的,不知如何下手,所以主管親自修改了代碼。

//  新手的代碼
public class ImageLoader {
    //  內(nèi)存緩存
    ImageCache mImageCache = new ImageCache();
    //  本地緩存
    DiskCache mDiskCache = new DiskCache();
    //  雙緩存
    DoubleCache mDoubleCache = new DoubleCache();
    
    //  ....省略下面代碼
}
//  主管的代碼
//  首先提取出了一個圖片緩存接口
public interface ImageCache {
    void put(String url, Bitmap bitmap);
    Bitmap get(String url);
}

//  然后分別實現(xiàn)了MemoryCache,DiskCache,DoubleCache,代碼省略

public class ImageLoader {
    //  圖片緩存,并設(shè)置了內(nèi)存緩存為默認(rèn)方式
    private ImageCache mImageCache = new MemoryCache();
    
    //  注入緩存實現(xiàn)  利用了向上轉(zhuǎn)型
    public void setImageCache(ImageCache imageCache) {
        mImageCache = imageCache;
    }

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

//  使用方法  只是通過傳入不同實現(xiàn)就可以切換緩存方式
ImageLoader loader = new ImageLoader();
loader.setImageCache(new MemoryCache());
loader.setImageCache(new DiskCache());
loader.setImageCache(new DoubleCache());

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

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

3.里氏替換原則

定義:所有引用的基類的地方都必須能透明的使用其子類的對象。
作者用了一句很通俗的話講解了這個原則——只要父類能出現(xiàn)打的地方之類就可以出現(xiàn)。

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

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

開閉原則和里氏替換原則往往都是一同出現(xiàn)的,通過里氏替換原則達到對擴展的開發(fā),對修改關(guān)閉的效果。

4.依賴倒置原則

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

依賴倒置原則的三個關(guān)鍵點:
(1).高層次模塊不應(yīng)該依賴于底層模塊,兩者都應(yīng)該依賴其抽象;
(2).抽象不應(yīng)依賴細(xì)節(jié);
(3).細(xì)節(jié)應(yīng)該依賴抽象。

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

所以作者說依賴倒置原則在Java中表現(xiàn)就是:模塊間依賴是通過抽象發(fā)生的,實現(xiàn)類之間并不產(chǎn)生直接依賴關(guān)系,其依賴關(guān)系是通過接口或抽象類產(chǎn)生的。
一句話概括:面向接口編程,或者說面向抽象編程。

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

//  如果在ImageLoader中直接這樣寫的話
//  就是直接依賴于細(xì)節(jié)(直接依賴實現(xiàn)類)
private DoubleCache mImageCache = new DoubleCache();
//  而主管的代碼卻直接完成1.2.3.4這是個原則
//  依賴于抽象,通過向上轉(zhuǎn)型,有一個默認(rèn)的實現(xiàn)類
private ImageCache mImageCache = new MemoryCache();

//  設(shè)置緩存策略,依賴于抽象
public void setImageCache(ImageCache imageCache) {
    mImageCache = imageCache;
} 

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

5.接口隔離原則

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

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

作者舉了一個例子,當(dāng)我們在使用流的時候我們需要在finally中判斷是否為空,如果不為空需要close()它,但每次使用流,都這么寫,也會讓代碼變得不優(yōu)美,這個時候我們考慮借助外力,就比如Java為我們提供了一個Closeable接口,而它有100多個實現(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();
               }
        }
}

//  寫了個CloseUtil類,然后西面提供這個靜態(tài)方法,所有實現(xiàn)了Closeable的類都可以調(diào)用這個方法
 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ù)就一個抽象,做到了我只需要知道這個對象是可關(guān)閉的,其他一概不管辛,也就是作者所說的接口隔離原則。

6.迪米特原則

定義:一個對象應(yīng)該對其他對象有最少的了解。

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

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

作者舉了例子方便我們理解,現(xiàn)在有三個類,一個是房間類,中介類以及租客類,租過房子的朋友都應(yīng)該知道,中介手里是有很多是房子的,而我們想要找什么樣的房子只要告訴中介條件,他就會幫你找到合適的房子,這里也是,在租客類眼中他的直接朋友就是中介類,我所有的找房子,打掃房間,修電器,交水電費都找他一個類就可以,因為他會幫我們都搞定,至于房東長什么樣子,房產(chǎn)證放在哪里了租客就都不需要關(guān)心了,這樣就形成了租客只和中介打交道,而中介管理者房屋列表,這也就是迪米特原則。

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,578評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,701評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,691評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,974評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,694評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,026評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,015評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,193評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,719評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,442評論 3 360
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,668評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,151評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,846評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,255評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,592評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,394評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,635評論 2 380

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