如何通俗理解設計模式及其思想?

本文由玉剛說寫做平臺提供寫做贊助html

原做者:卻把清梅嗅java

版權聲明:本文版權歸微信公衆號玉剛說全部,未經許可,不得以任何形式轉載git

術與道

數據結構,算法,設計模式被認爲是程序員必備技能的三叉戟,若是說編程語言的語法特性和業務編碼能力是【術】,那麼這三者能夠稱得上是【道】——【術】可讓你在IT行業賴以生存,而【道】則決定你將來在技術這條道路上能夠走多遠。程序員

邊學邊忘的窘境

先自我介紹一下。github

我是一個兩年工做經驗的Android開發人員,就像不少同行同樣,對於數據結構,算法,設計模式這些被奉爲程序員必修的三門內功,幾乎沒有去系統性地學習過(曾經專業課的數據結構,現在也基本還給了老師)。面試

你問我想不想當一個稱職的程序員,固然!數據結構,算法以及設計模式不止一次的被我列入學習計劃中,我認爲這是有意義而且必須的,而且,我也嘗試在閒暇之餘去主動學習它們。算法

可是很遺憾,至今爲止,有關於數據結構和算法,我依然處於入門階段,緣由就是:數據庫

我學會沒多久,一直沒機會用到,而後就忘了!編程

若是您翻看過我以前的 相關博客 或者我 Github這個倉庫,就能發現,我曾經關於數據結構和算法,都作出過嘗試,可是遺憾的是,我並不能學以至用 ——它們匆匆而來,匆匆而去,就好像生命中的過客同樣。設計模式

至於設計模式,由於空閒時間學習的時間並很少,雖然我也常常經過博客或者書籍學習設計模式,可是結果依然沒有太大的改變:過於抽象的概念,加上不常用,讓我很快把這些概念忘得差很少了。

我以爲這種學習方式效率不是很高,因而我決定換一種學習的方式——經過閱讀Github上那些開源庫的源碼,學習其中的設計思想和理念。

動機

和大多數人同樣,我只是在編程行業衆多平凡的開發者中的一員,在我職業生涯的伊始,我沒有接觸過技術大牛, 可是閱讀源碼可讓我零距離碰撞全球行業最頂尖工程師們的思想,我認爲對我而言這是一種效果不錯的學習方式,讓我受益不淺。

事實上,在要寫這篇博客的時候,我依然忐忑不安,我擔憂一篇文章若是寫的不夠好,會給讀者帶來誤導。

我最終鼓起勇氣寫這篇文章的目的是:我想經過分享我的對於設計模式的理解,以及本身的學習方式和所得,這種學習方式可能並不是適用於全部人,可是它至少能給予須要的人一個參考

設計模式的分類

咱們先看設計模式的分類:

範圍 建立型 結構型 行爲型
Factory Method(工廠方法) Adapter(類) (適配器) Interpreter(解釋器)
Template Method(模版方法)
對象 Abstract Factory(抽象工廠)
Builder(建造者)
Prototype(原型)
Singleton(單例)
Bridge(橋接)
Composite(組合)
Decorator(裝飾者)
Façade(外觀)
Flyweight(享元)
Proxy(代理)
Chain of Responsibility(職責鏈)
Command(命令)
Iterator(迭代器)
Mediator(中介者)
Memento(備忘錄)
Observer(觀察者)
State(狀體)
Strategy(策略)
Visitor(訪問者)

這是我從 這篇文章 中找到的對設計模式的概括。

同時,咱們須要瞭解到,設計模式的6個基本原則(這裏先列出來,接下來會參考案例一個個解釋):

  • 單一職責原則(Single Responsibility Principle)
  • 里氏代換原則(Liskov Substitution Principle)
  • 依賴倒轉原則(Dependence Inversion Principle)
  • 接口隔離原則(Interface Segregation Principle)
  • 迪米特法則,又稱最少知道原則(Demeter Principle)
  • 開閉原則(Open Close Principle)

在設計模式的學習過程當中,這些設計模式並不是是按照不一樣類型按部就班講解的,更多的場景是,多個不一樣類型的設計模式相互組合——最終展現出來的是一個完整的架構設計體系。這種設計模式複雜組合帶來的好處是:高內聚,低耦合,這使得庫自己的拓展很是簡單,同時也很是便於單元測試。

固然,對於經過源碼,想一窺設計思想的學習者來講,額外的接口,以及可能隨之額來額外的代碼會須要更多的學習成本,對於最初的我來講,複雜的設計真的給我帶來了很大的困擾,我試圖去理解和反思這樣設計的好處——它的確花費了我更多的時間,可是更讓我受益不淺。

最初的收穫——建立型模式

在Android學習的過程當中,我最早接觸到的就是建立型模式,所謂建立型模式,天然與對象的建立有關。

實際上,不談源碼,實際開發中,咱們也遇到了不少建立型模式的體現,最多見的當屬單例模式建造者模式(Builder)

1.「最簡單」的設計模式

咱們以單例模式爲例,他的定義是:

「一個類有且僅有一個實例,而且自行實例化向整個系統提供。」

相信你們對這個單例模式並不陌生,它被稱爲 「設計模式中最簡單的形式之一」,它很簡單,而且易於理解,開發者總能遇到須要持有惟一對象的業務需求。

以Android開發爲例,常常須要在某個類中,使用到Application對象,它自己是惟一的,所以咱們只須要經過一個類持有它的靜態引用,而後經過靜態方法獲取就能夠了。

另外的一種需求是,某個類的對象會佔用很大的內存,咱們也沒有必要對這個類實例化兩次,這樣,保持其對象的類單例,可以省下更多的性能空間,好比Android的數據庫db的引用。

實際上,單例模式的細分下來,有不少種實現方式,好比衆所周知的懶漢式餓漢式Double CheckLock靜態內部類枚舉,這些不一樣的單例實現方式,都有各自的優缺點(好比是否線程安全),也對應着不一樣的適用場景,這也正是單例模式做爲看起來「最簡單」同時也是面試中的重點考察項目的緣由。

這些不一樣的實現方式,百度上講解的很是詳細,本文不贅述。

咱們須要理解的是,咱們何時使用單例模式

對於系統中的某些類來講,只有一個實例很重要,好比上述的Application,這很好理解,實際上,在開發過程當中,咱們更須要關注一些細節的實現。

好比對Gson的單例。

實際開發中,調用Gson對象進行轉換的地方很是多,若是在調用的地方每次new Gson的話,是影響性能的。

Gson自己是線程安全的,它能夠被多個線程同時使用,所以,我更傾向於經過下面的方式獲取Gson的實例:

public class Gsons {

    private static class Holder {
        private static final Gson INSTANCE = new Gson();
    }

    public static Gson getInstance() {
        return Holder.INSTANCE;
    }
}
複製代碼

不只是Gson, 除此以外還有好比網絡請求的相關管理類(Retrofit對象,ServiceManager等),Android系統提供的各類XXXManager(NotificationManager)等等,這些經過單例的方式去管理它,可以讓你業務設計的更加嚴謹。

2.Builder的鏈式調用

建造者模式(Builder Pattern)使用多個簡單的對象一步一步構建成一個複雜的對象。

Android開發者必定很熟悉它,由於咱們建立AlertDialog的時候,鏈式調用的API實在是賞心悅目:

new AlertDialog
   .Builder(this)
   .setTitle("標題")
   .setMessage("內容")
   .setNegativeButton("取消", new DialogInterface.OnClickListener() {
       @Override
       public void onClick(DialogInterface dialog, int which) {
            //...
       }
   })
   .setPositiveButton("肯定", new DialogInterface.OnClickListener() {
       @Override
       public void onClick(DialogInterface dialog, int which) {
            //....
       }
   })
   .create()
   .show();
複製代碼

此外,稍微細心的同窗會發現,其實JDK中,StringBuilder和StringBuffer的源碼中的append()方法也是Builder模式的體現:

public StringBuilder append(String str) {
        super.append(str);  // 調用基類的append方法
        return this;
}

// 基類的append方法
public AbstractStringBuilder append(String str) {
       if (str == null) str = "null";
       int len = str.length();
       ensureCapacityInternal(count + len);
       str.getChars(0, len, value, count);
       count += len;
       return this; // 返回構建對象
}
複製代碼

除了賞心悅目的代碼以外,我更關注Builder模式的使用場景:

當咱們面臨着一個複雜對象的建立工做,其一般由各個部分的子對象用必定的算法構成;因爲需求的變化,這個複雜對象的各個部分常常面臨着劇烈的變化,可是將它們組合在一塊兒的算法卻相對穩定。

很好,我把 這個學習網站 關於Builder模式的適用場景複製下了下來,我曾經在學習它的時候嘗試去理解它的敘述,所得出的結論是 ——上文的定義很是嚴謹,可是我看不懂。

咱們參考AlertDialog,對於一個Dialog而言,它的基本構成是複雜的(有標題,內容,按鈕及其對應的事件等等屬性),可是在實際需求中,不一樣的界面,咱們須要展現給用戶的Dialog是不同的(標題不同,內容不同,點擊事件也不同),這些各個部分都是在不斷劇烈的變化,可是他們組合起來是相對穩定的(就是一個Dialog彈出展現在界面上)。

在這種狀況下,咱們能夠嘗試使用Builder模式,和普通的構造器生成對象不一樣,若是沒有需求,咱們能夠忽略配置某些屬性——對於Dialog,我能夠不去定義title,也能夠不去定義取消按鈕的點擊事件,他們內部都有默認的處理;此外,對於API的設計來說,Builder模式更利於去擴展新的功能或者屬性。

Builder模式在咱們開發中很是常見,除上述案例以外,Android流行的圖片加載庫,以及Notification通知的實例化等等,都能看到Builder的身影。

上文說到,Builder模式對於對象的建立提供了很是賞心悅目的API,我理解了Builder模式的思想和實現方式以後,便嘗試給本身的一些工具類加一些這樣的設計。

很快,我遇到了一個問題,那就是——這樣寫太TM累了!

3.避免過分設計

關於過分設計的定義,請參考 什麼是軟件開發中的過分設計? 的解釋,我認爲講解的很是風趣且易懂。

從我我的的角度而言,我遇到了問題,我嘗試給一些工具改成Builder實現,結果是,我添加了不少不少代碼,可是效果平平。

不只如此,這樣的設計給個人工具帶來了更多的複雜度,原本一個構造器new一下能解決的問題,非要不少行代碼鏈式配置,這種設計,作了還不如不作。

這樣的結果,讓我對網絡上一位前輩的總結很是贊同,那就是:

設計模式的一個重要的做用是代碼複用,最終的目的是提高效率。 因此,一個模式是否適合或必要,只要看看它是否能減小咱們的工做,提高咱們的工做效率

那麼,如何避免過分設計,個人經驗告訴我,寫代碼以前多思考,考慮不一樣實現方式所需的成本,保證代碼的不斷迭代和調整

即便如此,在開發的過程當中,過分設計仍然是難以免的狀況,只有依靠經驗的積累和不斷的總結思考,慢慢調整和豐富本身的我的經驗了。

4. 單一職責原則與依賴注入

對於單例模式,我彷佛也會遇到過分設計這種狀況——每一個對象的單例都須要再寫一個類去封裝,彷佛也太麻煩了。

實際上這並不是過分設計,由於這種設計是必要的,它可以節省性能的開銷,可是對象的建立和管理依然是對開發者一個不可小覷的工做量。

此外,還須要考量的是,對於一個複雜的單例對象,它可能有不少的狀態和依賴,這意味着,單例類的職責頗有可能很,這在必定程度上違背了單一職責原則

一個類只負責一個功能領域中的相應職責,或者能夠定義爲:就一個類而言,應該只有一個引發它變化的緣由。

單一職責原則告訴咱們:一個類不能太「累」! 一個類的職責越(這每每從構造器所須要的依賴就能體現出來),它被複用的可能性就越小。

在瞭解了單例模式的優勢和缺點後,咱們能夠有選擇的使用單例模式,對於依賴過於複雜的對象的單例,咱們更須要仔細考量。

對於複雜的依賴管理,依賴注入庫(好比Dagger)是一個能夠考慮的解決方案(慎重),對於單例模式的實現,你只須要在Module中對應的依賴Provider上添加一個@Singleton註解,編譯器會在編譯期間爲您自動生成對應的單例模式代碼。

不可否認,這個工具須要相對較高的學習成本,可是學會了依賴注入工具並理解了IOC(控制反轉)和DI(依賴注入)的思想以後,它將成爲你開發過程當中無往不勝的利器。

5.開閉原則

開閉原則:一個軟件應對擴展開放、對修改關閉,用head first中的話說就是:代碼應該如晚霞中 的蓮花同樣關閉(免於改變),如晨曦中的蓮花同樣開放(可以擴展).

建造者模式(Builder)即是開閉原則的徹底體現,它將對象的構建調用隔離開來,不一樣的使用者均可以經過自由的構建對象,而後使用它。

6.小結

建立型模式是最容易入門的,由於該類型的模式,更常常暴露在開發者面前,可是它們並不簡單,咱們除了知道這些模式的使用方式,更應該去思考何時用,用哪一個,甚至是組合使用它們——它們有些互斥,有些也能夠互補,這須要咱們去研究更經典的一些代碼,並本身做出嘗試。

不僅是建立型,接下來的結構型和行爲型的設計模式,本文也不會去一一闡述其目錄下全部的設計模式。

結構型模式

1.定義

首先闡述書中結構型模式的定義:

結構型模式涉及到如何組合類和對象以得到更大的結構。結構型類模式採用繼承機制來組合接口或實現。

在學習之初,對我我的而言,閱讀《設計模式:可複用面向對象軟件的基礎》 的內容宛如誦讀天書,書中對每種設計模式都進行了詳細的講解,可是我看完以後,很快就忘掉了,亦或是對看起來很是類似的兩種設計模式感到疑惑——書中的講解細緻入微,可是太抽象了。

最終(也就是如今),我我的對於結構型模式的理解是,經過將不一樣類或對象的組合,採用繼承或者組合接口,或者組合一些對象,以實現新的功能

用一句話陳述,就是對不一樣職責的對象(以對象/抽象類/接口的形式)之間組合調度的實現方式。

2.並不是全部對象的組合都是結構型模式

實際上,並不是全部對對象的組合都屬於結構型模式,構型模式的意義在於,對一些對象的組合,以實現新功能的方式—— 經過運行時,經過改變組合的關係,這種靈活性產生不一樣的效果,這種機制,普通的對象組合是不可能實現的。

接下來我將經過闡述數種不一樣的結構型模式在實際開發中的應用,逐步加深對上文敘述的理解。

3.RecyclerView:適配器模式

RecyclerView是Android平常開發中實現列表的首選方案,站在個人角度來看,我還沒想明白一個問題,RecyclerView是如何實現列表的?

我能夠回答說,經過實現RecyclerView.Adapter就能實現列表呀!

事實上,是這樣的,可是這引起了另一個問題,Adapter和RecyclerView之間的關係是什麼,爲啥實現了Adapter就能實現RecyclerView呢?

思考現實中的一個問題,我有一臺筆記本電腦,個人屋子裏也有一個電源,我如何給個人筆記本充電?

不假思索,咱們用筆記本的充電器鏈接電源和筆記本就好了,實際上,充電器更官方的叫法應該叫作電源適配器(Adapter)。對於筆記本電腦和電源來說,它們並無直接的關係,可是經過Adapter適配器,它們就能產生新的功能——電源給筆記本充電。

RecyclerView和數據的展現也是同樣,數據對象和RecyclerView並無直接的關係,可是我若是想要將數據展現在RecyclerView上,經過給RecyclerView配置一個適配器(Adapter)以鏈接數據源,就能夠了。

如今咱們來看Adapter模式的定義:

使本來因爲接口不兼容而不能一塊兒工做的那些類能夠一塊兒工做。

如今咱們理解了適配器模式的應用場景,可是我想拋出一個問題:

爲啥我要實現一個Adapter,設計之初,爲何不能直接設置RecyclerView呢?

好比說,我既然有了數據源,爲何設計之初,不能讓RecyclerView經過這樣直接配置呢:

mRecyclerView.setDataAndShow(datas);
複製代碼

個人理解是,若是把RecyclerView比喻爲屋子裏的電源插口,電源不知道它將要鏈接什麼設備(一樣,RecyclerView也不可能知道它要展現什麼樣的數據,怎麼展現),而不一樣的設備的接口也可能不同,可是隻要爲設備配置一個對應的適配器,兩個不相關的接口就能一塊兒工做。

RecyclerView的設計者將實現對開發者隱藏,並經過Adapter對開發者暴露其接口,開發者經過配置數據源(設備)和對應的適配器(充電器),就能實現列表的展現(充電)。

4.Retrofit:外觀模式與動態代理

說到迪米特法則(也叫最少知識原則),這個應該很好理解,就是下降各模塊之間的耦合:

迪米特法則:一個軟件實體應當儘量少地與其餘實體發生做用。

個人學習過程當中,讓我感覺到設計模式的組合之美的第一個庫就是Retrofit,對於網絡請求,你只須要配置一個接口:

public interface BlogService {

    @GET("blog/{id}")
    Call<ResponseBody> getBlog(@Path("id") int id);
}

// 使用方式
// 1.初始化配置Retrofit對象
Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://localhost:4567/")
        .addConverterFactory(GsonConverterFactory.create())
        .build();
// 2.實例化BlogService接口 
BlogService service = retrofit.create(BlogService.class);
複製代碼

Retrofit的源碼中,經過組合,將各類設計模式應用在一塊兒,構成了整個框架,保證了咱們常說的高內聚,低耦合,堪稱設計模式學習案例的典範,以下圖(圖片參考感謝這篇文章):

在分析整個框架的時候,咱們首先從API的使用方式入手,咱們能夠看到,在配置Retrofit的時候,庫採用了外觀模式做爲Retrofit的門面。

有朋友說了,在我看來,Retrofit的初始化,不該該是Builder模式嗎,爲何你說它是外觀模式呢?

咱們首先看一下《設計模式:可複用面向對象軟件的基礎》一書對於外觀模式的定義:

爲子系統中的一組接口提供一個一致的界面,外觀模式定義一個高層接口,這個接口使得這一子系統更容易使用。

個人解讀是,對於網絡請求庫的Retrofit,它內部有着不少不一樣的組件,包括數據的序列化,線程的調度,不一樣的適配器等,這一系列複雜的子系統,對於網絡請求來說,都是不可或缺的且關係複雜的,那麼,經過將它們都交給Retrofit對象配置調度(固然,Retrofit對象的建立是經過Builder模式實現的),對於API的調用者來講,使用配置起來簡單方便,這符合外觀模式 的定義。

簡單理解了外觀模式的思想,接下來咱們來看一下動態代理,對於最初接觸Retrofit的我來講,我最難以理解的是我只配置了一個接口,Retrofit是如何幫我把Service對象建立出來的呢?

// 2.實例化BlogService接口 
BlogService service = retrofit.create(BlogService.class);
複製代碼

實際上,並無BlogService這個對象的建立,service只不過是在jvm運行時動態生成的一個proxy對象,這個proxy對象的意義是:

爲其餘對象提供一種代理以控制對這個對象的訪問。

我想經過BlogService進行網絡請求,Retrofit就會經過動態代理實現一個proxy對象代理BlogService的行爲,當我調用它的某個方法請求網絡時,其實是這個proxy對象經過解析你的註解方法的參數,經過一系列的邏輯包裝成一個網絡請求的OkHttpCall對象,並請求網絡。

如今我明白了,怪不得我不管怎麼給Service的接口和方法命名,Retrofit都會動態生成代理對象並在調用其方法時進行解析,對於複雜多變的網絡請求來說,這種實現的方式很是合適。

5.里氏替換原則

在優秀的源碼中,咱們常常能夠看到,不少功能的實現,都是依賴其接口進行的,這裏咱們首先要理解面向對象中最重要的基本原則之一里氏替換原則

任何基類能夠出現的地方,子類必定能夠出現。

里氏代換原則是對開閉原則的補充。實現開閉原則的關鍵步驟就是抽象化。而基類與子類的繼承關係就是抽象化的具體實現,因此里氏代換原則是對實現抽象化的具體步驟的規範。

向上轉型是Java的基礎,咱們常常也用到,實際上,在進行設計的時候,儘可能從抽象類繼承,而不是從具體類繼承。同時,保證在軟件系統中,把父類都替換成它的子類,程序的行爲沒有變化,就足夠了。

6.小結

經過上述案例,咱們簡單理解了幾種結構型設計模式的概念和思想,總結一下:

在解決了對象的建立問題以後,對象的組成以及對象之間的依賴關係就成了開發人員關注的焦點,由於如何設計對象的結構、繼承和依賴關係會影響到後續程序的維護性、代碼的健壯性、耦合性等。因此也有多種結構型模式可供開發人員選擇使用。

提升類之間的協做效率——行爲型模式

1.定義

咱們先看書中對行爲型模式比較嚴謹的定義:

行爲模式涉及到算法和對象間職責的分配,行爲模式不只描述對象或類的模式,還描述它們之間的通訊模式。這些模式刻劃了在運行時難以跟蹤的複雜的控制流,將你的注意力從控制流轉移到對象間的聯繫方式上來。

依然是有點難以理解,咱們先舉兩個例子:

2.OkHttp:Intercepter和職責鏈模式

Okhttp 中, Intercepter就是典型的職責鏈模式的體現.它能夠設置任意數量的Intercepter來對網絡請求及其響應作任何中間處理——設置緩存, Https的證書驗證, 統一對請求加密/防串改, 打印自定義Log, 過濾請求等。

new OkHttpClient.Builder()
              .addNetworkInterceptor(interceptor1)
              .addNetworkInterceptor(interceptor2)
              .addNetworkInterceptor(interceptor3)
複製代碼

職責鏈模式的定義爲:

讓多個對象都有機會處理請求,從而避免請求的發送者和接受者之間的耦合關係,將他們連成一條鏈,並沿着這條鏈傳遞該請求,直到有對象處理它爲止。

以現實爲例,職責鏈模式之一就是網絡鏈接,七層或五層的網絡鏈接模型以下:

  • 網絡請求發出,通過應用層->傳輸層->網絡層->鏈接層->物理層

  • 收到響應後,物理層->鏈接層->網絡層->傳輸層->應用層

在請求通過各層時,由每層輪流處理.每層均可以對請求或響應進行處理.並能夠中斷連接,以自身爲終點返回響應。

3.RxJava:觀察者模式

Android開發中,點擊事件的監聽是很經典觀察者模式的體現:

button.setOnClickListener(v -> {
    // do something
})
複製代碼

對設置OnClickListener來講,View是被觀察者,OnClickListener是觀察者,二者經過setOnClickListener()方法達成註冊(訂閱)關係。訂閱以後,當用戶點擊按鈕,View就會將點擊事件發送給已經註冊的 OnClickListener。

一樣,對於能夠和Retrofit配套的RxJava來說,它是也經過觀察者模式來實現的。

// 被觀察者
Observable observable = Observable
                          .just("Hello", "Hi", "Aloha")

// 觀察者
Observer<String> observer = new Observer<String>() {
    @Override
    public void onNext(String s) {
        Log.d(tag, "Item: " + s);
    }

    @Override
    public void onCompleted() {
        Log.d(tag, "Completed!");
    }

    @Override
    public void onError(Throwable e) {
        Log.d(tag, "Error!");
    }
};

// 執行訂閱關係
observable.subscribe(observer);
複製代碼

RxJava強大的異步處理,將數據的建立接收分紅了兩部分,對於觀察者來講,它不關心數據何時發射的,怎麼發射的,它只關心,當觀察到到最新數據時,怎樣進行對應的處理。

咱們知道了觀察者模式的這種方式,咱們更須要去深刻思考對於觀察者模式使用先後,對咱們代碼設計系統的影響——它的好處是什麼?

最直接的好處是,被觀察者並不知道觀察者的詳細實現

就像我剛纔所說的,被觀察者只負責發射事件,對於事件如何處理,它並不關心,這意味着被觀察者觀察者之間並非緊密耦合的,它們能夠處於一個系統中的不一樣抽象層次。

不一樣抽象層次這句話自己就有點抽象,咱們以Button的點擊事件爲例,對於Button來說,它是一個庫的工具,它應該屬於項目中底層組件,而對於咱們某個Activity的某個點擊事件來說,它是屬於靠頂部業務層的代碼,能夠說,Button和點擊事件是不在一個抽象層次較低層次的Button能夠將點擊事件發送給較高層次的事件監聽器並通知它。

而若是不採用這種方式,觀察者和被觀察者就必須混在一塊兒,這樣對象就會橫貫項目的2個層次(違反了層次性),或者必須放在這兩層中的某一層中(可能會損害層次抽象)。

底層組件按鈕被點擊後行爲,抽象出來交給較高層級去實現,瞭解了這種方式的好處,依賴倒置原則就不難理解了。

4.依賴倒置原則

如今咱們來了解一下依賴倒置原則

抽象不該該依賴於細節,細節應當依賴於抽象。換言之,要針對接口編程,而非針對實現編程。

它的原則是:

  • 1.高層模塊不該該依賴於低層模塊,兩個都應該依賴於抽象。
  • 2.抽象不該該依賴細節,細節應該依賴於抽象。

在java中,抽象指的是接口或者抽象類,細節就是具體的實現類,使用接口或者抽象類的目的是制定好規範,而不去涉及任何具體的操做,把展示細節的任務交給他們的實現類去完成。

瞭解了依賴倒置原則,咱們再接再礪,學習最後一個設計模式的基本原則:

5.接口隔離原則

接口隔離原則:客戶端不該該依賴它不須要的接口;一個類對另外一個類的依賴應該創建在最小的接口上。

這個應該是最好理解的原則了,它的意義就是:使用多個專門的接口比使用單一的總接口要好

這很好理解,對於鳥的實現(Bird),咱們能夠定義兩個功能接口,分別是Fly和Eat,咱們可讓Bird分別實現這兩個接口——若是咱們還有一個Dog,那麼對於Eat接口,能夠複用,可是若是隻有一個接口(包含Fly和Eat兩個功能),對於Dog來講,它是不會飛(Fly)的,那麼就須要針對Dog再聲明一個新的接口,這是沒有必要的設計。

6.小結

在對象的結構和對象的建立問題都解決了以後,就剩下對象的行爲問題了,若是對象的行爲設計的好,那麼對象的行爲就會更清晰,它們之間的協做效率就會提升。

如今咱們再看上文中對行爲型模式比較嚴謹的定義,相信你們可以理解一些了:

行爲模式涉及到算法和對象間職責的分配,行爲模式不只描述對象或類的模式,還描述它們之間的通訊模式。這些模式刻劃了在運行時難以跟蹤的複雜的控制流,將你的注意力從控制流轉移到對象間的聯繫方式上來。

喘口氣

關於設計模式相關的講解內容,到此基本就告一段落了。

等等...

我一個設計模式都沒學會,你TM告訴我你講完了?

一無所得?

本文並無去經過代碼簡單描述各個設計模式的實現方式,於我而言毫無心義,設計思想是經過不斷思考,理解並在親身嘗試中學會的,短期快速大量閱讀學習的方式效果甚微,即便學會了,如何將sample中的設計思想,轉換爲實際產品中複雜的業務設計,這也是一個很是大的難題。

在這裏,我引用《倚天屠龍記》中我最喜歡的經典片斷, 就是張三丰在武當山當着敵人的面教張無忌太極劍那段。

只聽張三丰問道:‘孩兒,你看清楚了沒有?’張無忌道:‘看清楚了。’張三丰道: ‘都記得了沒有?’張無忌道:‘已忘記了一小半。’張三丰道:‘好,那也難爲了你。你本身去想一想罷。’張無忌低頭默想。過了一會,張三丰問道:‘現下怎樣了?’張無忌道: ‘已忘記了一大半。’

周顛失聲叫道:‘糟糕!愈來愈忘記得多了。張真人,你這路劍法非常深奧,看一遍怎能記得?請你再使一遍給咱們教主瞧瞧罷。’

張三丰微笑道:‘好,我再使一遍。’提劍出招,演將起來。衆人只看了數招,心下大奇,原來第二次所使,和第一次使的居然沒一招相同。周顛叫道:‘糟糕,糟糕!這可更加叫人胡塗啦。’張三丰畫劍成圈,問道:‘孩兒,怎樣啦?’張無忌道:‘還有三招沒忘記。’張三丰點點頭,收劍歸座。

張無忌在殿上緩緩踱了一個圈子,沉思半晌,又緩緩踱了半個圈子,擡起頭來,滿臉喜色,叫道:‘這我可全忘了,忘得乾乾淨淨的了。’張三丰道:‘不壞不壞!忘得真快,你這就請八臂神劍指教罷!’

總結

關於設計模式,個人理解是,不要拘泥於其概念,只有深入理解了其設計的思想,理解以後,親自去嘗試使用它,在使用的過程當中加深對這種思想的理解,我想比經過書籍或者博客一個一個的去學,效果要更好。

我學習它們的方式是,學習一些優秀開源庫的源碼,思考爲何這裏使用這些設計模式,以後再參考《設計模式》一書的相關概念,最後本身去嘗試並加深理解。

於我而言,這種方式的短板在於剛開始的時候,可能須要更多的時間去學習,不少庫的源碼在初接觸時,並不是容易理解,可是好處是,當學會以後,這種思想就很難再從你的記憶中跑掉了,並且,在寫代碼時,會下意識嘗試使用這些模式(固然,更多狀況是打開2個窗口,一邊參考源碼,一邊學習將其融入到本身的代碼中)。

設計模式相對於分類和概念正如太極拳(劍),它更是一種思想,一種應對變化的解決思想——我不認爲本文本身的學習方式和心得,可以適合每個正在閱讀本文的讀者,可是若是本文對您對技術成長的道路上有一些幫助,對我而言這就是值得的。

參考

1.菜鳥教程——設計模式:
http://www.runoob.com/design-pattern/design-pattern-tutorial.html

2.《設計模式:可複用面向對象軟件的基礎》
伽瑪等著;李英軍等譯,機械工業出版社,2015年12月初版37次印刷

3.Retrofit分析-漂亮的解耦套路: https://blog.piasy.com/2016/06/25/Understand-Retrofit/

4.建立型模式、結構型模式和行爲型模式之間的關係 https://blog.csdn.net/qq_34583891/article/details/70853637

歡迎關注個人微信公衆號,接收第一手技術乾貨
相關文章
相關標籤/搜索