如何通俗理解設(shè)計(jì)模式及其思想

本文由 玉剛說(shuō)寫作平臺(tái) 提供寫作贊助
原作者:卻把清梅嗅
原文地址:https://mp.weixin.qq.com/s/T6ZCQRydzFgVUezlywC8Zw
版權(quán)聲明:本文版權(quán)歸微信公眾號(hào) 玉剛說(shuō) 所有园匹,未經(jīng)許可钉鸯,不得以任何形式轉(zhuǎn)載!

術(shù)與道


數(shù)據(jù)結(jié)構(gòu)音同,算法千扶,設(shè)計(jì)模式被認(rèn)為是程序員必備技能的三叉戟料祠,如果說(shuō)編程語(yǔ)言的語(yǔ)法特性和業(yè)務(wù)編碼能力是【術(shù)】,那么這三者可以稱得上是【道】——【術(shù)】可以讓你在IT行業(yè)賴以生存澎羞,而【道】則決定你未來(lái)在技術(shù)這條道路上可以走多遠(yuǎn)髓绽。

邊學(xué)邊忘的窘境


先自我介紹一下。

我是一個(gè)兩年工作經(jīng)驗(yàn)的Android開發(fā)人員妆绞,就像很多同行一樣顺呕,對(duì)于數(shù)據(jù)結(jié)構(gòu),算法括饶,設(shè)計(jì)模式這些被奉為程序員必修的三門內(nèi)功株茶,幾乎沒有去系統(tǒng)性地學(xué)習(xí)過(曾經(jīng)專業(yè)課的數(shù)據(jù)結(jié)構(gòu),如今也基本還給了老師)图焰。

你問我想不想當(dāng)一個(gè)稱職的程序員启盛,當(dāng)然!數(shù)據(jù)結(jié)構(gòu)技羔,算法以及設(shè)計(jì)模式不止一次的被我列入學(xué)習(xí)計(jì)劃中僵闯,我認(rèn)為這是有意義并且必須的,并且藤滥,我也嘗試在閑暇之余去主動(dòng)學(xué)習(xí)它們棍厂。

但是很遺憾,至今為止超陆,有關(guān)于數(shù)據(jù)結(jié)構(gòu)和算法,我依然處于入門階段浦马,原因就是:

我學(xué)會(huì)沒多久时呀,一直沒機(jī)會(huì)用到,然后就忘了晶默!

如果您翻看過我之前的 相關(guān)博客 或者我 Github這個(gè)倉(cāng)庫(kù)谨娜,就能發(fā)現(xiàn),我曾經(jīng)關(guān)于數(shù)據(jù)結(jié)構(gòu)和算法磺陡,都做出過嘗試趴梢,但是遺憾的是漠畜,我并不能學(xué)以致用 ——它們匆匆而來(lái),匆匆而去坞靶,就好像生命中的過客一樣憔狞。

至于設(shè)計(jì)模式,因?yàn)榭臻e時(shí)間學(xué)習(xí)的時(shí)間并不多彰阴,雖然我也經(jīng)常通過博客或者書籍學(xué)習(xí)設(shè)計(jì)模式瘾敢,但是結(jié)果依然沒有太大的改變:過于抽象的概念,加上不經(jīng)常使用尿这,讓我很快把這些概念忘得差不多了簇抵。

我覺得這種學(xué)習(xí)方式效率不是很高,于是我決定換一種學(xué)習(xí)的方式——通過閱讀Github上那些開源庫(kù)的源碼射众,學(xué)習(xí)其中的設(shè)計(jì)思想和理念碟摆。

動(dòng)機(jī)


和大多數(shù)人一樣,我只是在編程行業(yè)眾多平凡的開發(fā)者中的一員叨橱,在我職業(yè)生涯的伊始典蜕,我沒有接觸過技術(shù)大牛, 但是閱讀源碼可以讓我零距離碰撞全球行業(yè)內(nèi)最頂尖工程師們的思想,我認(rèn)為對(duì)我而言這是一種效果不錯(cuò)的學(xué)習(xí)方式雏逾,讓我受益匪淺嘉裤。

事實(shí)上,在要寫這篇博客的時(shí)候栖博,我依然忐忑不安屑宠,我擔(dān)心一篇文章如果寫的不夠好,會(huì)給讀者帶來(lái)誤導(dǎo)仇让。

我最終鼓起勇氣寫這篇文章的目的是:我想通過分享個(gè)人對(duì)于設(shè)計(jì)模式的理解典奉,以及自己的學(xué)習(xí)方式和所得,這種學(xué)習(xí)方式可能并非適用于所有人丧叽,但是它至少能給予需要的人一個(gè)參考卫玖。

設(shè)計(jì)模式的分類


我們先看設(shè)計(jì)模式的分類:

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

這是我從 這篇文章 中找到的對(duì)設(shè)計(jì)模式的歸納。

同時(shí)踊淳,我們需要了解到假瞬,設(shè)計(jì)模式的6個(gè)基本原則(這里先列出來(lái),接下來(lái)會(huì)參考案例一個(gè)個(gè)解釋):

  • 1迂尝、單一職責(zé)原則(Single Responsibility Principle)
  • 2脱茉、里氏代換原則(Liskov Substitution Principle)
  • 3、依賴倒轉(zhuǎn)原則(Dependence Inversion Principle)
  • 4垄开、接口隔離原則(Interface Segregation Principle)
  • 5琴许、迪米特法則,又稱最少知道原則(Demeter Principle)
  • 6溉躲、開閉原則(Open Close Principle)

在設(shè)計(jì)模式的學(xué)習(xí)過程中榜田,這些設(shè)計(jì)模式并非是按照不同類型循序漸進(jìn)講解的益兄,更多的場(chǎng)景是,多個(gè)不同類型的設(shè)計(jì)模式相互組合——最終展示出來(lái)的是一個(gè)完整的架構(gòu)設(shè)計(jì)體系箭券。這種設(shè)計(jì)模式復(fù)雜組合帶來(lái)的好處是:高內(nèi)聚净捅,低耦合,這使得庫(kù)本身的拓展非常簡(jiǎn)單邦鲫,同時(shí)也非常便于單元測(cè)試灸叼。

當(dāng)然,對(duì)于通過源碼庆捺,想一窺設(shè)計(jì)思想的學(xué)習(xí)者來(lái)說(shuō)古今,額外的接口,以及可能隨之額來(lái)額外的代碼會(huì)需要更多的學(xué)習(xí)成本滔以,對(duì)于最初的我來(lái)說(shuō)捉腥,復(fù)雜的設(shè)計(jì)真的給我?guī)?lái)了很大的困擾,我試圖去理解和反思這樣設(shè)計(jì)的好處——它的確花費(fèi)了我更多的時(shí)間你画,但是更讓我受益匪淺抵碟。

最初的收獲——?jiǎng)?chuàng)建型模式


在Android學(xué)習(xí)的過程中,我最先接觸到的就是創(chuàng)建型模式坏匪,所謂創(chuàng)建型模式拟逮,自然與對(duì)象的創(chuàng)建有關(guān)。

實(shí)際上适滓,不談源碼敦迄,實(shí)際開發(fā)中,我們也遇到了很多創(chuàng)建型模式的體現(xiàn)凭迹,最常見的當(dāng)屬單例模式建造者模式(Builder)罚屋。

1.“最簡(jiǎn)單”的設(shè)計(jì)模式

我們以單例模式為例,他的定義是:

“一個(gè)類有且僅有一個(gè)實(shí)例嗅绸,并且自行實(shí)例化向整個(gè)系統(tǒng)提供脾猛。”

相信大家對(duì)這個(gè)單例模式并不陌生鱼鸠,它被稱為 “設(shè)計(jì)模式中最簡(jiǎn)單的形式之一”猛拴,它很簡(jiǎn)單,并且易于理解蚀狰,開發(fā)者總能遇到需要持有唯一對(duì)象的業(yè)務(wù)需求漆弄。

以Android開發(fā)為例,經(jīng)常需要在某個(gè)類中造锅,使用到Application對(duì)象,它本身是唯一的廉邑,因此我們只需要通過一個(gè)類持有它的靜態(tài)引用哥蔚,然后通過靜態(tài)方法獲取就可以了倒谷。

另外的一種需求是,某個(gè)類的對(duì)象會(huì)占用很大的內(nèi)存糙箍,我們也沒有必要對(duì)這個(gè)類實(shí)例化兩次渤愁,這樣,保持其對(duì)象的類單例深夯,能夠省下更多的性能空間抖格,比如Android的數(shù)據(jù)庫(kù)db的引用。

實(shí)際上咕晋,單例模式的細(xì)分下來(lái)雹拄,有很多種實(shí)現(xiàn)方式,比如眾所周知的懶漢式掌呜,餓漢式滓玖,Double CheckLock靜態(tài)內(nèi)部類质蕉,枚舉势篡,這些不同的單例實(shí)現(xiàn)方式,都有各自的優(yōu)缺點(diǎn)(比如是否線程安全)模暗,也對(duì)應(yīng)著不同的適用場(chǎng)景禁悠,這也正是單例模式作為看起來(lái)“最簡(jiǎn)單”同時(shí)也是面試中的重點(diǎn)考察項(xiàng)目的原因。

這些不同的實(shí)現(xiàn)方式兑宇,百度上講解的非常詳細(xì)碍侦,本文不贅述。

我們需要理解的是顾孽,我們什么時(shí)候使用單例模式祝钢。

對(duì)于系統(tǒng)中的某些類來(lái)說(shuō),只有一個(gè)實(shí)例很重要若厚,比如上述的Application拦英,這很好理解,實(shí)際上测秸,在開發(fā)過程中疤估,我們更需要關(guān)注一些細(xì)節(jié)的實(shí)現(xiàn)。

比如對(duì)Gson的單例霎冯。

實(shí)際開發(fā)中铃拇,調(diào)用Gson對(duì)象進(jìn)行轉(zhuǎn)換的地方非常多,如果在調(diào)用的地方每次new Gson的話沈撞,是影響性能的慷荔。

Gson本身是線程安全的,它可以被多個(gè)線程同時(shí)使用缠俺,因此显晶,我更傾向于通過下面的方式獲取Gson的實(shí)例:

public class Gsons {

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

    public static Gson getInstance() {
        return Holder.INSTANCE;
    }
}

不僅是Gson, 除此之外還有比如網(wǎng)絡(luò)請(qǐng)求的相關(guān)管理類(Retrofit對(duì)象贷岸,ServiceManager等),Android系統(tǒng)提供的各種XXXManager(NotificationManager)等等磷雇,這些通過單例的方式去管理它偿警,能夠讓你業(yè)務(wù)設(shè)計(jì)的更加嚴(yán)謹(jǐn)。

2.Builder的鏈?zhǔn)秸{(diào)用

建造者模式(Builder Pattern)使用多個(gè)簡(jiǎn)單的對(duì)象一步一步構(gòu)建成一個(gè)復(fù)雜的對(duì)象唯笙。

Android開發(fā)者一定很熟悉它螟蒸,因?yàn)槲覀儎?chuàng)建AlertDialog的時(shí)候,鏈?zhǔn)秸{(diào)用的API實(shí)在是賞心悅目:

new AlertDialog
   .Builder(this)
   .setTitle("標(biāo)題")
   .setMessage("內(nèi)容")
   .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();

此外崩掘,稍微細(xì)心的同學(xué)會(huì)發(fā)現(xiàn)七嫌,其實(shí)JDK中,StringBuilder和StringBuffer的源碼中的append()方法也是Builder模式的體現(xiàn):

public StringBuilder append(String str) {
        super.append(str);  // 調(diào)用基類的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; // 返回構(gòu)建對(duì)象
}

除了賞心悅目的代碼之外呢堰,我更關(guān)注Builder模式的使用場(chǎng)景:

當(dāng)我們面臨著一個(gè)復(fù)雜對(duì)象的創(chuàng)建工作抄瑟,其通常由各個(gè)部分的子對(duì)象用一定的算法構(gòu)成;由于需求的變化枉疼,這個(gè)復(fù)雜對(duì)象的各個(gè)部分經(jīng)常面臨著劇烈的變化皮假,但是將它們組合在一起的算法卻相對(duì)穩(wěn)定。

很好骂维,我把 這個(gè)學(xué)習(xí)網(wǎng)站 關(guān)于Builder模式的適用場(chǎng)景復(fù)制下了下來(lái)惹资,我曾經(jīng)在學(xué)習(xí)它的時(shí)候嘗試去理解它的敘述,所得出的結(jié)論是 ——上文的定義非常嚴(yán)謹(jǐn)航闺,但是我看不懂褪测。

我們參考AlertDialog,對(duì)于一個(gè)Dialog而言潦刃,它的基本構(gòu)成是復(fù)雜的(有標(biāo)題侮措,內(nèi)容,按鈕及其對(duì)應(yīng)的事件等等屬性)乖杠,但是在實(shí)際需求中分扎,不同的界面,我們需要展示給用戶的Dialog是不一樣的(標(biāo)題不一樣胧洒,內(nèi)容不一樣畏吓,點(diǎn)擊事件也不一樣),這些各個(gè)部分都是在不斷劇烈的變化卫漫,但是他們組合起來(lái)是相對(duì)穩(wěn)定的(就是一個(gè)Dialog彈出展示在界面上)菲饼。

在這種情況下,我們可以嘗試使用Builder模式列赎,和普通的構(gòu)造器生成對(duì)象不同宏悦,如果沒有需求,我們可以忽略配置某些屬性——對(duì)于Dialog,我可以不去定義title饼煞,也可以不去定義取消按鈕的點(diǎn)擊事件辫塌,他們內(nèi)部都有默認(rèn)的處理;此外派哲,對(duì)于API的設(shè)計(jì)來(lái)講,Builder模式更利于去擴(kuò)展新的功能或者屬性掺喻。

Builder模式在我們開發(fā)中非常常見芭届,除上述案例之外,Android流行的圖片加載庫(kù)感耙,以及Notification通知的實(shí)例化等等褂乍,都能看到Builder的身影。

上文說(shuō)到即硼,Builder模式對(duì)于對(duì)象的創(chuàng)建提供了非常賞心悅目的API逃片,我理解了Builder模式的思想和實(shí)現(xiàn)方式之后,便嘗試給自己的一些工具類加一些這樣的設(shè)計(jì)只酥。

很快褥实,我遇到了一個(gè)問題,那就是——這樣寫太TM累了裂允!

3.避免過度設(shè)計(jì)

關(guān)于過度設(shè)計(jì)的定義损离,請(qǐng)參考 什么是軟件開發(fā)中的過度設(shè)計(jì)? 的解釋绝编,我認(rèn)為講解的非常風(fēng)趣且易懂僻澎。

從我個(gè)人的角度而言,我遇到了問題十饥,我嘗試給一些工具改為Builder實(shí)現(xiàn)窟勃,結(jié)果是,我添加了很多很多代碼逗堵,但是效果平平秉氧。

不僅如此,這樣的設(shè)計(jì)給我的工具帶來(lái)了更多的復(fù)雜度砸捏,本來(lái)一個(gè)構(gòu)造器new一下能解決的問題谬运,非要很多行代碼鏈?zhǔn)脚渲茫@種設(shè)計(jì)垦藏,做了還不如不做梆暖。

這樣的結(jié)果,讓我對(duì)網(wǎng)絡(luò)上一位前輩的總結(jié)非常贊同掂骏,那就是:

設(shè)計(jì)模式的一個(gè)重要的作用是代碼復(fù)用轰驳,最終的目的是提升效率。
所以,一個(gè)模式是否適合或必要级解,只要看看它是否能減少我們的工作冒黑,提升我們的工作效率

那么勤哗,如何避免過度設(shè)計(jì)抡爹,我的經(jīng)驗(yàn)告訴我,寫代碼之前多思考芒划,考慮不同實(shí)現(xiàn)方式所需的成本冬竟,保證代碼的不斷迭代和調(diào)整

即使如此民逼,在開發(fā)的過程中泵殴,過度設(shè)計(jì)仍然是難以避免的情況,只有依靠經(jīng)驗(yàn)的積累和不斷的總結(jié)思考拼苍,慢慢調(diào)整和豐富自己的個(gè)人經(jīng)驗(yàn)了笑诅。

4. 單一職責(zé)原則與依賴注入

對(duì)于單例模式,我似乎也會(huì)遇到過度設(shè)計(jì)這種情況——每個(gè)對(duì)象的單例都需要再寫一個(gè)類去封裝疮鲫,似乎也太麻煩了吆你。

實(shí)際上這并非過度設(shè)計(jì),因?yàn)檫@種設(shè)計(jì)是必要的棚点,它能夠節(jié)省性能的開銷早处,但是對(duì)象的創(chuàng)建和管理依然是對(duì)開發(fā)者一個(gè)不可小覷的工作量。

此外瘫析,還需要考量的是砌梆,對(duì)于一個(gè)復(fù)雜的單例對(duì)象,它可能有很多的狀態(tài)和依賴贬循,這意味著咸包,單例類的職責(zé)很有可能很,這在一定程度上違背了單一職責(zé)原則

一個(gè)類只負(fù)責(zé)一個(gè)功能領(lǐng)域中的相應(yīng)職責(zé)杖虾,或者可以定義為:就一個(gè)類而言烂瘫,應(yīng)該只有一個(gè)引起它變化的原因。

單一職責(zé)原則告訴我們:一個(gè)類不能太“累”奇适! 一個(gè)類的職責(zé)越(這往往從構(gòu)造器所需要的依賴就能體現(xiàn)出來(lái))坟比,它被復(fù)用的可能性就越小。

在了解了單例模式的優(yōu)點(diǎn)和缺點(diǎn)后嚷往,我們可以有選擇的使用單例模式葛账,對(duì)于依賴過于復(fù)雜的對(duì)象的單例,我們更需要仔細(xì)考量皮仁。

對(duì)于復(fù)雜的依賴管理籍琳,依賴注入庫(kù)(比如Dagger)是一個(gè)可以考慮的解決方案(慎重)菲宴,對(duì)于單例模式的實(shí)現(xiàn),你只需要在Module中對(duì)應(yīng)的依賴Provider上添加一個(gè)@Singleton注解趋急,編譯器會(huì)在編譯期間為您自動(dòng)生成對(duì)應(yīng)的單例模式代碼喝峦。

不能否認(rèn),這個(gè)工具需要相對(duì)較高的學(xué)習(xí)成本呜达,但是學(xué)會(huì)了依賴注入工具并理解了IOC(控制反轉(zhuǎn))谣蠢,DI(依賴注入)的思想之后,它將成為你開發(fā)過程中無(wú)往不勝的利器查近。

5.開閉原則

開閉原則:一個(gè)軟件應(yīng)對(duì)擴(kuò)展開放漩怎、對(duì)修改關(guān)閉,用head first中的話說(shuō)就是:代碼應(yīng)該如晚霞中 的蓮花一樣關(guān)閉(免于改變)嗦嗡,如晨曦中的蓮花一樣開放(能夠擴(kuò)展).

建造者模式(Builder)便是開閉原則的完全體現(xiàn),它將對(duì)象的構(gòu)建調(diào)用隔離開來(lái)饭玲,不同的使用者都可以通過自由的構(gòu)建對(duì)象侥祭,然后使用它。

6.小結(jié)

創(chuàng)建型模式是最容易入門的茄厘,因?yàn)樵擃愋偷哪J桨?jīng)常暴露在開發(fā)者面前,但是它們并不簡(jiǎn)單次哈,我們除了知道這些模式的使用方式胎署,更應(yīng)該去思考什么時(shí)候用,用哪個(gè)窑滞,甚至是組合使用它們——它們有些互斥琼牧,有些也可以互補(bǔ),這需要我們?nèi)パ芯扛?jīng)典的一些代碼哀卫,并自己作出嘗試巨坊。

不只是創(chuàng)建型,接下來(lái)的結(jié)構(gòu)型和行為型的設(shè)計(jì)模式此改,本文也不會(huì)去一一闡述其目錄下所有的設(shè)計(jì)模式趾撵。

結(jié)構(gòu)型模式


1.定義

首先闡述書中結(jié)構(gòu)型模式的定義:

結(jié)構(gòu)型模式涉及到如何組合類和對(duì)象以獲得更大的結(jié)構(gòu)。結(jié)構(gòu)型類模式采用繼承機(jī)制來(lái)組合接口或?qū)崿F(xiàn)共啃。

在學(xué)習(xí)之初占调,對(duì)我個(gè)人而言,閱讀《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》 的內(nèi)容宛如誦讀天書移剪,書中對(duì)每種設(shè)計(jì)模式都進(jìn)行了詳細(xì)的講解究珊,但是我看完之后,很快就忘掉了挂滓,亦或是對(duì)看起來(lái)非常相似的兩種設(shè)計(jì)模式感到疑惑——書中的講解細(xì)致入微苦银,但是太抽象了啸胧。

最終(也就是現(xiàn)在),我個(gè)人對(duì)于結(jié)構(gòu)型模式的理解是幔虏,通過將不同類或?qū)ο蟮慕M合纺念,采用繼承或者組合接口,或者組合一些對(duì)象想括,以實(shí)現(xiàn)新的功能陷谱。

用一句話陳述,就是對(duì)不同職責(zé)的對(duì)象(以對(duì)象/抽象類/接口的形式)之間組合調(diào)度的實(shí)現(xiàn)方式瑟蜈。

2.并非所有對(duì)象的組合都是結(jié)構(gòu)型模式

實(shí)際上烟逊,并非所有對(duì)對(duì)象的組合都屬于結(jié)構(gòu)型模式,構(gòu)型模式的意義在于铺根,對(duì)一些對(duì)象的組合宪躯,以實(shí)現(xiàn)新功能的方式—— 通過運(yùn)行時(shí),通過改變組合的關(guān)系位迂,這種靈活性產(chǎn)生不同的效果访雪,這種機(jī)制,普通的對(duì)象組合是不可能實(shí)現(xiàn)的掂林。

接下來(lái)我將通過闡述數(shù)種不同的結(jié)構(gòu)型模式在實(shí)際開發(fā)中的應(yīng)用臣缀,逐步加深對(duì)上文敘述的理解。

3.RecyclerView:適配器模式

RecyclerView是Android日常開發(fā)中實(shí)現(xiàn)列表的首選方案泻帮,站在我的角度來(lái)看精置,我還沒想明白一個(gè)問題,RecyclerView是如何實(shí)現(xiàn)列表的锣杂?

我可以回答說(shuō)脂倦,通過實(shí)現(xiàn)RecyclerView.Adapter就能實(shí)現(xiàn)列表呀!

事實(shí)上元莫,是這樣的狼讨,但是這引發(fā)了另外一個(gè)問題,Adapter和RecyclerView之間的關(guān)系是什么柒竞,為啥實(shí)現(xiàn)了Adapter就能實(shí)現(xiàn)RecyclerView呢政供?

思考現(xiàn)實(shí)中的一個(gè)問題,我有一臺(tái)筆記本電腦朽基,我的屋子里也有一個(gè)電源布隔,我如何給我的筆記本充電?

不假思索稼虎,我們用筆記本的充電器連接電源和筆記本就行了衅檀,實(shí)際上,充電器更官方的叫法應(yīng)該叫做電源適配器(Adapter)霎俩。對(duì)于筆記本電腦和電源來(lái)講哀军,它們并沒有直接的關(guān)系沉眶,但是通過Adapter適配器,它們就能產(chǎn)生新的功能——電源給筆記本充電杉适。

RecyclerView和數(shù)據(jù)的展示也是一樣谎倔,數(shù)據(jù)對(duì)象和RecyclerView并沒有直接的關(guān)系,但是我如果想要將數(shù)據(jù)展示在RecyclerView上猿推,通過給RecyclerView配置一個(gè)適配器(Adapter)以連接數(shù)據(jù)源片习,就可以了。

現(xiàn)在我們來(lái)看Adapter模式的定義:

使原本由于接口不兼容而不能一起工作的那些類可以一起工作蹬叭。

現(xiàn)在我們理解了適配器模式的應(yīng)用場(chǎng)景藕咏,但是我想拋出一個(gè)問題:

為啥我要實(shí)現(xiàn)一個(gè)Adapter,設(shè)計(jì)之初秽五,為什么不能直接設(shè)置RecyclerView呢孽查?

比如說(shuō),我既然有了數(shù)據(jù)源坦喘,為什么設(shè)計(jì)之初卦碾,不能讓RecyclerView通過這樣直接配置呢:

mRecyclerView.setDataAndShow(datas);

我的理解是,如果把RecyclerView比喻為屋子里的電源插口起宽,電源不知道它將要連接什么設(shè)備(同樣,RecyclerView也不可能知道它要展示什么樣的數(shù)據(jù)济榨,怎么展示)坯沪,而不同的設(shè)備的接口也可能不一樣,但是只要為設(shè)備配置一個(gè)對(duì)應(yīng)的適配器擒滑,兩個(gè)不相關(guān)的接口就能一起工作腐晾。

RecyclerView的設(shè)計(jì)者將實(shí)現(xiàn)對(duì)開發(fā)者隱藏,并通過Adapter對(duì)開發(fā)者暴露其接口丐一,開發(fā)者通過配置數(shù)據(jù)源(設(shè)備)和對(duì)應(yīng)的適配器(充電器)藻糖,就能實(shí)現(xiàn)列表的展示(充電)。

4.Retrofit:外觀模式與動(dòng)態(tài)代理

說(shuō)到迪米特法則(也叫最少知識(shí)原則)库车,這個(gè)應(yīng)該很好理解,就是降低各模塊之間的耦合:

迪米特法則:一個(gè)軟件實(shí)體應(yīng)當(dāng)盡可能少地與其他實(shí)體發(fā)生作用巨柒。

我的學(xué)習(xí)過程中,讓我感受到設(shè)計(jì)模式的組合之美的第一個(gè)庫(kù)就是Retrofit柠衍,對(duì)于網(wǎng)絡(luò)請(qǐng)求洋满,你只需要配置一個(gè)接口:

public interface BlogService {

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

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

Retrofit的源碼中,通過組合珍坊,將各種設(shè)計(jì)模式應(yīng)用在一起牺勾,構(gòu)成了整個(gè)框架,保證了我們常說(shuō)的高內(nèi)聚阵漏,低耦合驻民,堪稱設(shè)計(jì)模式學(xué)習(xí)案例的典范翻具,如下圖(圖片參考感謝這篇文章):

在分析整個(gè)框架的時(shí)候,我們首先從API的使用方式入手回还,我們可以看到裆泳,在配置Retrofit的時(shí)候,庫(kù)采用了外觀模式作為Retrofit的門面懦趋。

有朋友說(shuō)了晾虑,在我看來(lái),Retrofit的初始化仅叫,不應(yīng)該是Builder模式嗎帜篇,為什么你說(shuō)它是外觀模式呢?

我們首先看一下《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》一書對(duì)于外觀模式的定義:

為子系統(tǒng)中的一組接口提供一個(gè)一致的界面诫咱,外觀模式定義一個(gè)高層接口笙隙,這個(gè)接口使得這一子系統(tǒng)更容易使用。

我的解讀是,對(duì)于網(wǎng)絡(luò)請(qǐng)求庫(kù)的Retrofit,它內(nèi)部有著很多不同的組件滥玷,包括數(shù)據(jù)的序列化鉴未,線程的調(diào)度,不同的適配器等往史,這一系列復(fù)雜的子系統(tǒng),對(duì)于網(wǎng)絡(luò)請(qǐng)求來(lái)講,都是不可或缺的且關(guān)系復(fù)雜的,那么拾给,通過將它們都交給Retrofit對(duì)象配置調(diào)度(當(dāng)然祥得,Retrofit對(duì)象的創(chuàng)建是通過Builder模式實(shí)現(xiàn)的),對(duì)于API的調(diào)用者來(lái)說(shuō)蒋得,使用配置起來(lái)簡(jiǎn)單方便级及,這符合外觀模式 的定義。

簡(jiǎn)單理解了外觀模式的思想额衙,接下來(lái)我們來(lái)看一下動(dòng)態(tài)代理饮焦,對(duì)于最初接觸Retrofit的我來(lái)說(shuō),我最難以理解的是我只配置了一個(gè)接口窍侧,Retrofit是如何幫我把Service對(duì)象創(chuàng)建出來(lái)的呢追驴?

// 2.實(shí)例化BlogService接口        
BlogService service = retrofit.create(BlogService.class);

實(shí)際上,并沒有BlogService這個(gè)對(duì)象的創(chuàng)建疏之,service只不過是在jvm運(yùn)行時(shí)動(dòng)態(tài)生成的一個(gè)proxy對(duì)象殿雪,這個(gè)proxy對(duì)象的意義是:

為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問。

我想通過BlogService進(jìn)行網(wǎng)絡(luò)請(qǐng)求锋爪,Retrofit就會(huì)通過動(dòng)態(tài)代理實(shí)現(xiàn)一個(gè)proxy對(duì)象代理BlogService的行為丙曙,當(dāng)我調(diào)用它的某個(gè)方法請(qǐng)求網(wǎng)絡(luò)時(shí)爸业,實(shí)際上是這個(gè)proxy對(duì)象通過解析你的注解方法的參數(shù),通過一系列的邏輯包裝成一個(gè)網(wǎng)絡(luò)請(qǐng)求的OkHttpCall對(duì)象亏镰,并請(qǐng)求網(wǎng)絡(luò)扯旷。

現(xiàn)在我明白了,怪不得我無(wú)論怎么給Service的接口和方法命名索抓,Retrofit都會(huì)動(dòng)態(tài)生成代理對(duì)象并在調(diào)用其方法時(shí)進(jìn)行解析钧忽,對(duì)于復(fù)雜多變的網(wǎng)絡(luò)請(qǐng)求來(lái)講,這種實(shí)現(xiàn)的方式非常合適逼肯。

5.里氏替換原則

在優(yōu)秀的源碼中耸黑,我們經(jīng)常可以看到篮幢,很多功能的實(shí)現(xiàn)大刊,都是依賴其接口進(jìn)行的,這里我們首先要理解面向?qū)ο笾凶钪匾幕驹瓌t之一里氏替換原則

任何基類可以出現(xiàn)的地方三椿,子類一定可以出現(xiàn)缺菌。

里氏代換原則是對(duì)開閉原則的補(bǔ)充。實(shí)現(xiàn)開閉原則的關(guān)鍵步驟就是抽象化搜锰。而基類與子類的繼承關(guān)系就是抽象化的具體實(shí)現(xiàn)伴郁,所以里氏代換原則是對(duì)實(shí)現(xiàn)抽象化的具體步驟的規(guī)范。

向上轉(zhuǎn)型是Java的基礎(chǔ)蛋叼,我們經(jīng)常也用到焊傅,實(shí)際上,在進(jìn)行設(shè)計(jì)的時(shí)候鸦列,盡量從抽象類繼承,而不是從具體類繼承鹏倘。同時(shí)薯嗤,保證在軟件系統(tǒng)中,把父類都替換成它的子類纤泵,程序的行為沒有變化骆姐,就足夠了。

6.小結(jié)

通過上述案例捏题,我們簡(jiǎn)單理解了幾種結(jié)構(gòu)型設(shè)計(jì)模式的概念和思想玻褪,總結(jié)一下:

在解決了對(duì)象的創(chuàng)建問題之后,對(duì)象的組成以及對(duì)象之間的依賴關(guān)系就成了開發(fā)人員關(guān)注的焦點(diǎn)公荧,因?yàn)槿绾卧O(shè)計(jì)對(duì)象的結(jié)構(gòu)带射、繼承和依賴關(guān)系會(huì)影響到后續(xù)程序的維護(hù)性、代碼的健壯性循狰、耦合性等窟社。所以也有多種結(jié)構(gòu)型模式可供開發(fā)人員選擇使用券勺。

提高類之間的協(xié)作效率——行為型模式


1.定義

我們先看書中對(duì)行為型模式比較嚴(yán)謹(jǐn)?shù)亩x:

行為模式涉及到算法和對(duì)象間職責(zé)的分配,行為模式不僅描述對(duì)象或類的模式灿里,還描述它們之間的通信模式关炼。這些模式刻劃了在運(yùn)行時(shí)難以跟蹤的復(fù)雜的控制流,將你的注意力從控制流轉(zhuǎn)移到對(duì)象間的聯(lián)系方式上來(lái)匣吊。

依然是有點(diǎn)難以理解儒拂,我們先舉兩個(gè)例子:

2.OkHttp:Intercepter和職責(zé)鏈模式

Okhttp 中, Intercepter就是典型的職責(zé)鏈模式的體現(xiàn).它可以設(shè)置任意數(shù)量的Intercepter來(lái)對(duì)網(wǎng)絡(luò)請(qǐng)求及其響應(yīng)做任何中間處理——設(shè)置緩存, Https的證書驗(yàn)證, 統(tǒng)一對(duì)請(qǐng)求加密/防串改, 打印自定義Log, 過濾請(qǐng)求等。

new OkHttpClient.Builder()
              .addNetworkInterceptor(interceptor1)
              .addNetworkInterceptor(interceptor2)
              .addNetworkInterceptor(interceptor3)

職責(zé)鏈模式的定義為:

讓多個(gè)對(duì)象都有機(jī)會(huì)處理請(qǐng)求色鸳,從而避免請(qǐng)求的發(fā)送者和接受者之間的耦合關(guān)系社痛,將他們連成一條鏈,并沿著這條鏈傳遞該請(qǐng)求缕碎,直到有對(duì)象處理它為止褥影。

以現(xiàn)實(shí)為例,職責(zé)鏈模式之一就是網(wǎng)絡(luò)連接咏雌,七層或五層的網(wǎng)絡(luò)連接模型如下:

  • 網(wǎng)絡(luò)請(qǐng)求發(fā)出,經(jīng)過應(yīng)用層->傳輸層->網(wǎng)絡(luò)層->連接層->物理層

  • 收到響應(yīng)后,物理層->連接層->網(wǎng)絡(luò)層->傳輸層->應(yīng)用層

在請(qǐng)求經(jīng)過各層時(shí),由每層輪流處理.每層都可以對(duì)請(qǐng)求或響應(yīng)進(jìn)行處理.并可以中斷鏈接,以自身為終點(diǎn)返回響應(yīng)凡怎。

3.RxJava:觀察者模式

Android開發(fā)中,點(diǎn)擊事件的監(jiān)聽是很經(jīng)典觀察者模式的體現(xiàn):

button.setOnClickListener(v -> {
    // do something
})

對(duì)設(shè)置OnClickListener來(lái)說(shuō)赊抖,View是被觀察者统倒,OnClickListener是觀察者,兩者通過setOnClickListener()方法達(dá)成注冊(cè)(訂閱)關(guān)系氛雪。訂閱之后房匆,當(dāng)用戶點(diǎn)擊按鈕,View就會(huì)將點(diǎn)擊事件發(fā)送給已經(jīng)注冊(cè)的 OnClickListener报亩。

同樣浴鸿,對(duì)于可以和Retrofit配套的RxJava來(lái)講,它是也通過觀察者模式來(lái)實(shí)現(xiàn)的弦追。

// 被觀察者
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!");
    }
};

// 執(zhí)行訂閱關(guān)系
observable.subscribe(observer);

RxJava強(qiáng)大的異步處理岳链,將數(shù)據(jù)的創(chuàng)建接收分成了兩部分,對(duì)于觀察者來(lái)說(shuō)劲件,它不關(guān)心數(shù)據(jù)什么時(shí)候發(fā)射的掸哑,怎么發(fā)射的,它只關(guān)心零远,當(dāng)觀察到到最新數(shù)據(jù)時(shí)苗分,怎樣進(jìn)行對(duì)應(yīng)的處理。

我們知道了觀察者模式的這種方式牵辣,我們更需要去深入思考對(duì)于觀察者模式使用前后摔癣,對(duì)我們代碼設(shè)計(jì)系統(tǒng)的影響——它的好處是什么?

最直接的好處是,被觀察者并不知道觀察者的詳細(xì)實(shí)現(xiàn)供填。

就像我剛才所說(shuō)的拐云,被觀察者只負(fù)責(zé)發(fā)射事件,對(duì)于事件如何處理近她,它并不關(guān)心叉瘩,這意味著被觀察者觀察者之間并不是緊密耦合的,它們可以處于一個(gè)系統(tǒng)中的不同抽象層次粘捎。

不同抽象層次這句話本身就有點(diǎn)抽象薇缅,我們以Button的點(diǎn)擊事件為例,對(duì)于Button來(lái)講攒磨,它是一個(gè)庫(kù)的工具泳桦,它應(yīng)該屬于項(xiàng)目中底層組件,而對(duì)于我們某個(gè)Activity的某個(gè)點(diǎn)擊事件來(lái)講娩缰,它是屬于靠頂部業(yè)務(wù)層的代碼灸撰,可以說(shuō),Button和點(diǎn)擊事件是不在一個(gè)抽象層次拼坎,較低層次的Button可以將點(diǎn)擊事件發(fā)送給較高層次的事件監(jiān)聽器并通知它浮毯。

而如果不采用這種方式,觀察者和被觀察者就必須混在一起泰鸡,這樣對(duì)象就會(huì)橫貫項(xiàng)目的2個(gè)層次(違反了層次性)债蓝,或者必須放在這兩層中的某一層中(可能會(huì)損害層次抽象)。

底層組件按鈕被點(diǎn)擊后行為盛龄,抽象出來(lái)交給較高層級(jí)去實(shí)現(xiàn)饰迹,了解了這種方式的好處,依賴倒置原則就不難理解了余舶。

4.依賴倒置原則

現(xiàn)在我們來(lái)了解一下依賴倒置原則

抽象不應(yīng)該依賴于細(xì)節(jié)啊鸭,細(xì)節(jié)應(yīng)當(dāng)依賴于抽象。換言之匿值,要針對(duì)接口編程赠制,而非針對(duì)實(shí)現(xiàn)編程。

它的原則是:

  • 1.高層模塊不應(yīng)該依賴于低層模塊千扔,兩個(gè)都應(yīng)該依賴于抽象憎妙。
  • 2.抽象不應(yīng)該依賴細(xì)節(jié)库正,細(xì)節(jié)應(yīng)該依賴于抽象曲楚。

在java中,抽象指的是接口或者抽象類褥符,細(xì)節(jié)就是具體的實(shí)現(xiàn)類龙誊,使用接口或者抽象類的目的是制定好規(guī)范,而不去涉及任何具體的操作喷楣,把展現(xiàn)細(xì)節(jié)的任務(wù)交給他們的實(shí)現(xiàn)類去完成趟大。

了解了依賴倒置原則鹤树,我們?cè)俳釉賲枺瑢W(xué)習(xí)最后一個(gè)設(shè)計(jì)模式的基本原則:

5.接口隔離原則

接口隔離原則:客戶端不應(yīng)該依賴它不需要的接口逊朽;一個(gè)類對(duì)另一個(gè)類的依賴應(yīng)該建立在最小的接口上罕伯。

這個(gè)應(yīng)該是最好理解的原則了,它的意義就是:使用多個(gè)專門的接口比使用單一的總接口要好叽讳。

這很好理解追他,對(duì)于鳥的實(shí)現(xiàn)(Bird),我們可以定義兩個(gè)功能接口岛蚤,分別是Fly和Eat邑狸,我們可以讓Bird分別實(shí)現(xiàn)這兩個(gè)接口——如果我們還有一個(gè)Dog,那么對(duì)于Eat接口涤妒,可以復(fù)用单雾,但是如果只有一個(gè)接口(包含F(xiàn)ly和Eat兩個(gè)功能),對(duì)于Dog來(lái)說(shuō)她紫,它是不會(huì)飛(Fly)的硅堆,那么就需要針對(duì)Dog再聲明一個(gè)新的接口,這是沒有必要的設(shè)計(jì)犁苏。

6.小結(jié)

在對(duì)象的結(jié)構(gòu)和對(duì)象的創(chuàng)建問題都解決了之后硬萍,就剩下對(duì)象的行為問題了,如果對(duì)象的行為設(shè)計(jì)的好围详,那么對(duì)象的行為就會(huì)更清晰朴乖,它們之間的協(xié)作效率就會(huì)提高。

現(xiàn)在我們?cè)倏瓷衔闹袑?duì)行為型模式比較嚴(yán)謹(jǐn)?shù)亩x助赞,相信大家能夠理解一些了:

行為模式涉及到算法和對(duì)象間職責(zé)的分配买羞,行為模式不僅描述對(duì)象或類的模式,還描述它們之間的通信模式雹食。這些模式刻劃了在運(yùn)行時(shí)難以跟蹤的復(fù)雜的控制流畜普,將你的注意力從控制流轉(zhuǎn)移到對(duì)象間的聯(lián)系方式上來(lái)。

喘口氣


關(guān)于設(shè)計(jì)模式相關(guān)的講解內(nèi)容群叶,到此基本就告一段落了吃挑。

等等...

我一個(gè)設(shè)計(jì)模式都沒學(xué)會(huì),你TM告訴我你講完了街立?

一無(wú)所獲舶衬?

本文并沒有去通過代碼簡(jiǎn)單描述各個(gè)設(shè)計(jì)模式的實(shí)現(xiàn)方式,于我而言毫無(wú)意義赎离,設(shè)計(jì)思想是通過不斷思考逛犹,理解并在親身嘗試中學(xué)會(huì)的,短時(shí)間快速大量閱讀學(xué)習(xí)的方式效果甚微,即使學(xué)會(huì)了虽画,如何將sample中的設(shè)計(jì)思想舞蔽,轉(zhuǎn)換為實(shí)際產(chǎn)品中復(fù)雜的業(yè)務(wù)設(shè)計(jì),這也是一個(gè)非常大的難題码撰。

在這里渗柿,我引用《倚天屠龍記》中我最喜歡的經(jīng)典片段, 就是張三豐在武當(dāng)山當(dāng)著敵人的面教張無(wú)忌太極劍那段。

“只聽張三豐問道:‘孩兒脖岛,你看清楚了沒有做祝?’張無(wú)忌道:‘看清楚了〖Ω冢’張三豐道: ‘都記得了沒有混槐?’張無(wú)忌道:‘已忘記了一小半⌒裕’張三豐道:‘好声登,那也難為了你。你自己去想想罷揣苏∶跎ぃ’張無(wú)忌低頭默想。過了一會(huì)卸察,張三豐問道:‘現(xiàn)下怎樣了脯厨?’張無(wú)忌道: ‘已忘記了一大半】又剩’

周顛失聲叫道:‘糟糕合武!越來(lái)越忘記得多了。張真人涡扼,你這路劍法很是深?yuàn)W稼跳,看一遍怎能記得?請(qǐng)你再使一遍給我們教主瞧瞧罷吃沪√郎疲’

張三豐微笑道:‘好,我再使一遍票彪『斓’提劍出招,演將起來(lái)降铸。眾人只看了數(shù)招在旱,心下大奇,原來(lái)第二次所使垮耳,和第一次使的竟然沒一招相同颈渊。周顛叫道:‘糟糕,糟糕终佛!這可更加叫人胡涂啦俊嗽。’張三豐畫劍成圈铃彰,問道:‘孩兒绍豁,怎樣啦?’張無(wú)忌道:‘還有三招沒忘記牙捉≈褡幔’張三豐點(diǎn)點(diǎn)頭,收劍歸座邪铲。

張無(wú)忌在殿上緩緩踱了一個(gè)圈子芬位,沉思半晌,又緩緩踱了半個(gè)圈子带到,抬起頭來(lái)昧碉,滿臉喜色,叫道:‘這我可全忘了揽惹,忘得干干凈凈的了被饿。’張三豐道:‘不壞不壞搪搏!忘得真快狭握,你這就請(qǐng)八臂神劍指教罷!’”

總結(jié)


關(guān)于設(shè)計(jì)模式疯溺,我的理解是论颅,不要拘泥于其概念,只有深刻理解了其設(shè)計(jì)的思想囱嫩,理解之后嗅辣,親自去嘗試使用它,在使用的過程中加深對(duì)這種思想的理解挠说,我想比通過書籍或者博客一個(gè)一個(gè)的去學(xué)澡谭,效果要更好。

我學(xué)習(xí)它們的方式是损俭,學(xué)習(xí)一些優(yōu)秀開源庫(kù)的源碼蛙奖,思考為什么這里使用這些設(shè)計(jì)模式,之后再參考《設(shè)計(jì)模式》一書的相關(guān)概念杆兵,最后自己去嘗試并加深理解雁仲。

于我而言,這種方式的短板在于剛開始的時(shí)候琐脏,可能需要更多的時(shí)間去學(xué)習(xí)攒砖,很多庫(kù)的源碼在初接觸時(shí)缸兔,并非容易理解,但是好處是吹艇,當(dāng)學(xué)會(huì)之后惰蜜,這種思想就很難再?gòu)哪愕挠洃浿信艿袅耍沂苌瘢趯懘a時(shí)抛猖,會(huì)下意識(shí)嘗試使用這些模式(當(dāng)然,更多情況是打開2個(gè)窗口鼻听,一邊參考源碼财著,一邊學(xué)習(xí)將其融入到自己的代碼中)。

設(shè)計(jì)模式相對(duì)于分類和概念正如太極拳(劍)撑碴,它更是一種思想撑教,一種應(yīng)對(duì)變化的解決思想——我不認(rèn)為本文自己的學(xué)習(xí)方式和心得,能夠適合每一個(gè)正在閱讀本文的讀者醉拓,但是如果本文對(duì)您對(duì)技術(shù)成長(zhǎng)的道路上有一些幫助驮履,對(duì)我而言這就是值得的。

參考


1.菜鳥教程——設(shè)計(jì)模式:
http://www.runoob.com/design-pattern/design-pattern-tutorial.html

2.《設(shè)計(jì)模式:可復(fù)用面向?qū)ο筌浖幕A(chǔ)》
伽瑪?shù)戎溃焕钣④姷茸g玫镐,機(jī)械工業(yè)出版社,2015年12月第一版37次印刷

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

4.創(chuàng)建型模式怠噪、結(jié)構(gòu)型模式和行為型模式之間的關(guān)系
https://blog.csdn.net/qq_34583891/article/details/70853637

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末恐似,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子傍念,更是在濱河造成了極大的恐慌矫夷,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件憋槐,死亡現(xiàn)場(chǎng)離奇詭異双藕,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)阳仔,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門忧陪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人近范,你說(shuō)我怎么就攤上這事嘶摊。” “怎么了评矩?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵叶堆,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我斥杜,道長(zhǎng)虱颗,這世上最難降的妖魔是什么沥匈? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮忘渔,結(jié)果婚禮上高帖,老公的妹妹穿的比我還像新娘。我一直安慰自己辨萍,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布返弹。 她就那樣靜靜地躺著锈玉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪义起。 梳的紋絲不亂的頭發(fā)上拉背,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音默终,去河邊找鬼椅棺。 笑死,一個(gè)胖子當(dāng)著我的面吹牛齐蔽,可吹牛的內(nèi)容都是我干的两疚。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼含滴,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼诱渤!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起谈况,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤勺美,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后碑韵,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體赡茸,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年祝闻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了占卧。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡联喘,死狀恐怖屉栓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情耸袜,我是刑警寧澤友多,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站堤框,受9級(jí)特大地震影響域滥,放射性物質(zhì)發(fā)生泄漏纵柿。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一启绰、第九天 我趴在偏房一處隱蔽的房頂上張望昂儒。 院中可真熱鬧,春花似錦委可、人聲如沸渊跋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)拾酝。三九已至,卻和暖如春卡者,著一層夾襖步出監(jiān)牢的瞬間蒿囤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工崇决, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留材诽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓恒傻,卻偏偏與公主長(zhǎng)得像脸侥,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子盈厘,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,116評(píng)論 25 707
  • 我是一個(gè)殺手湿痢,今年34歲,還有一天我就35歲扑庞,而今天則是我當(dāng)殺手的最后一天譬重,我原本以為殺手是一個(gè)終生職業(yè),但我錯(cuò)了...
    月亮的尾巴閱讀 266評(píng)論 1 6
  • 當(dāng)你依賴別人時(shí)罐氨,也是把你自己的力量交出去了臀规,當(dāng)你依賴別人時(shí),你就是在說(shuō):你行栅隐,而我不行塔嬉;你是大人,我是小孩租悄。...
    侯君_6ef0閱讀 295評(píng)論 0 2
  • 蘇禾離開的這一天泣棋,恰好是冬至胶哲。 走之前,他對(duì)姜檸說(shuō):“我可能會(huì)晚點(diǎn)回來(lái)潭辈,給我留碗餃子鸯屿!” “好啊澈吨,”姜檸輕笑,“不...
    雀臨閱讀 421評(píng)論 0 3
  • 一桑阶、子宮肌瘤 根源:脾腎濕寒。 治療:桂枝茯苓丸勾邦。桂枝10克蚣录、茯苓12克、丹皮10克检痰、白芍12克包归、桃仁10克锨推。 該...
    郁茹閱讀 672評(píng)論 0 3