變化驅(qū)動:正交設(shè)計

一個出發(fā)點(diǎn)

當(dāng)談起軟件設(shè)計目的時钮糖,能夠獲得所有人認(rèn)同的答案只有一個:功能實現(xiàn)。 因為這是一個軟件存在的根本原因茫叭。

而在計算機(jī)軟件發(fā)展的初期会烙,這一點(diǎn)也正是所有人做軟件設(shè)計的唯一動機(jī)。因而韭赘,很自然的缩滨,整個軟件都被放在單一過程中,然后用到處存在的goto語句控制流程泉瞻。

盡管理論上講脉漏,任意復(fù)雜的系統(tǒng)都可以被放入同一個函數(shù)里。但隨著軟件越來復(fù)雜袖牙,即便是智商最為發(fā)達(dá)的程序員也發(fā)現(xiàn)侧巨,單一過程的復(fù)雜度已經(jīng)超出他的掌控極限。這逼迫人們必須對大問題進(jìn)行分解鞭达,分而治之司忱。

時至今日,盡管超大函數(shù)畴蹭,上帝類依然并不罕見坦仍,但當(dāng)大到一定程度,上帝類的創(chuàng)造者最終也會發(fā)現(xiàn)自己終究沒有上帝般的掌控力叨襟。因而繁扎,哪怕是軟件設(shè)計素養(yǎng)為負(fù)值的開發(fā)者,或多或少也會對一個復(fù)雜系統(tǒng)進(jìn)行一定程度的拆分芹啥。

這就是模塊化設(shè)計的最初動機(jī)锻离。

兩個問題

一旦人們開始進(jìn)行進(jìn)行模塊化拆分,就必須解決如下兩個問題:

  1. 究竟軟件模塊該怎樣劃分才是合理的?
  2. 將一個大單元劃分為多個小單元之后墓怀,它們之間必然要通過銜接點(diǎn)進(jìn)行合作汽纠。如果我們把這些銜接點(diǎn)看作API,那么問題就變?yōu)椋涸鯓佣xAPI才是合理的?

更簡單的說:怎么分傀履?然后再怎么合虱朵?

分工與合作
分工與合作

而這兩個問題的答案莉炉,正是現(xiàn)代軟件設(shè)計的核心關(guān)注點(diǎn)。

三方關(guān)系

為了找到這兩個問題的答案碴犬,我們需要重新回到最初的問題:為何要做軟件設(shè)計?

Kent Beck給出的答案是:軟件設(shè)計是為了在讓軟件在長期范圍內(nèi)容易應(yīng)對變化絮宁。

在這個精煉的定義中,包含著三個關(guān)鍵詞:長期服协,容易绍昂,變化。這意味著:

  1. 越是需要長期維護(hù)的項目偿荷,變化更多窘游,也更難預(yù)測變化的方式;
  2. 軟件設(shè)計,事關(guān)成本;
  3. 如何在難以預(yù)測的千變?nèi)f化中跳纳,保持低廉的變更成本忍饰,正是軟件設(shè)計要解決的問題。

對此寺庄,Kent Beck提出了一個更為精煉的原則:局部化影響艾蓝。意思是說:我們希望,任何一個變化斗塘,對于我們當(dāng)前的軟件設(shè)計影響范圍都可以控制在一個盡量小的局部赢织。

這當(dāng)然是所有嚴(yán)肅的軟件從業(yè)者都夢寐以求的。

可問題在于逛拱,如何才能做到?

內(nèi)聚與耦合

每個讀過基礎(chǔ)軟件工程教程的人都知道:一個易于應(yīng)對變化的軟件設(shè)計應(yīng)該遵從高內(nèi)聚敌厘,低耦合原則。

所謂內(nèi)聚性朽合,關(guān)注的是一個軟件單位內(nèi)部的關(guān)聯(lián)緊密程度俱两。因而高內(nèi)聚追求的是關(guān)聯(lián)緊密的事物應(yīng)該被放在一起,并且只有關(guān)聯(lián)緊密的事物才應(yīng)該被放在一起曹步。簡單說宪彩,就是Unix的設(shè)計哲學(xué):

Do One Thing, Do It Well。

耦合性讲婚,則是強(qiáng)調(diào)兩個或多個軟件單位之間的關(guān)聯(lián)緊密程度尿孔。因而低耦合追求的是,軟件單位之間盡可能不要相互影響筹麸。

這樣的解釋活合,對于很多人而言,依然會感到過于抽象物赶。但如果我們進(jìn)一步思考白指,就會意識到:看似神秘的內(nèi)聚耦合,正好對應(yīng)最初的兩個問題:

  1. 當(dāng)我們劃分模塊時酵紫,要讓每個模塊都盡可能高內(nèi)聚;
  2. 而當(dāng)我們定義模塊之間的API時告嘲,需要讓雙方盡可能低耦合错维。

如果用圖來展現(xiàn),就是下面的過程與關(guān)系:

高內(nèi)聚橄唬,低耦合
高內(nèi)聚赋焕,低耦合

這幅圖揭示了模塊化設(shè)計的全部:首先將一個低內(nèi)聚的模塊首先拆分為多個高內(nèi)聚的模塊;然后再考慮這多個模塊之間的API設(shè)計仰楚,以降低這些高內(nèi)聚的軟件單元之間的耦合度隆判。

除了內(nèi)聚耦合之外,上面這幅圖還揭示了另外一種關(guān)系:正交缸血。具備正交關(guān)系的兩個模塊蜜氨,可以做到一方的變化不會影響另外一方的變化。換句話說捎泻,雙方各自獨(dú)自變化,互不影響埋哟。

而這幅圖的右側(cè)笆豁,正是我們模塊化的目標(biāo)。它描述了永恒的三方關(guān)系客戶赤赊,API闯狱,實現(xiàn),以及它們之間的關(guān)系抛计。這個三方關(guān)系圖清晰的指出了我們應(yīng)該關(guān)注的內(nèi)聚性哄孤,耦合性,以及正交性都發(fā)生在何處吹截。

四個策略

相對于局部化影響瘦陈,高內(nèi)聚,低耦合原則已經(jīng)清晰和具體許多。但依然更像是在描述目標(biāo)或結(jié)果波俄,而沒有指明該如何達(dá)成的方法晨逝。雖然《代碼大全》列舉了那么多的內(nèi)聚性耦合性的分類,但對于想應(yīng)用它們的軟件設(shè)計人員懦铺,依然感覺如隔靴撓癢捉貌,不得要領(lǐng)。

因而冬念,我們需要從它推導(dǎo)出更為明確趁窃,更具指導(dǎo)性和操作性的設(shè)計原則。

為了做到這一點(diǎn)急前,我們必須首先搞清楚:內(nèi)聚耦合醒陆,和變化之間的關(guān)系是怎樣的,以至于高內(nèi)聚叔汁、低耦合的模塊化方式能夠更容易應(yīng)對變化统求?

我們再次回顧內(nèi)聚耦合的定義:它們是用來衡量代碼元素之間的關(guān)聯(lián)緊密程度检碗。很容易得知:元素之間的關(guān)聯(lián)緊密程度越高,一個變化引起它們相互之間都發(fā)生變化的可能性就越高码邻。反之折剃,關(guān)聯(lián)程度越弱,變化引起的連鎖變化的概率就越低像屋。

因而怕犁,我們要把容易互相影響的、關(guān)聯(lián)程度緊密的元素己莺,都封裝在一個模塊內(nèi)部(而這正是我們老生常談的封裝變化的動機(jī))奏甫;同時讓模塊之間的關(guān)聯(lián)緊密程度盡可能降低,以讓模塊間盡可能不要相互影響凌受。從而最終做到局部化影響阵子。

因而,Uncle Bob說:一個類只應(yīng)該有一個變化原因胜蛉。他進(jìn)一步談到:所謂一個變化原因挠进,指一個變化會導(dǎo)致整個類所包含的各個元素都要發(fā)生變化。為何會如此誊册?因為它們的關(guān)聯(lián)程度太緊密(因而高內(nèi)聚)领突,以至于牽一發(fā)而動全身。

因此案怯,Uncle Bob將職責(zé)定義為變化原因君旦。

在一些時候,我們可以直接判定一個模塊是否包含多重職責(zé)嘲碱。因為它們確實包含著明顯沒有什么關(guān)聯(lián)的兩組代碼元素金砍。

但在另外一些場景下,我們則無法清晰的判定:一個模塊是否真的包含多重變化原因悍汛,或多重職責(zé)捞魁。比如如下代碼:

struct Student 
{
  char name[MAX_NAME_LEN];
  unsigned int height; 
};

void sort_students_by_height(Student students[], size_t num_of_students) 
{
  for(size_t y=0; y < num_of_students-1; y++) 
  {
    for(size_t x=1; x < num_of_students - y; x++) 
    {
      if(students[x].height > students[x-1].height) 
      { 
        SWAP(students[x], students[x-1]);
      } 
    }
  } 
}

這是一個對學(xué)生按照身高從低到高進(jìn)行排序的算法。對于這段代碼离咐,如果我們進(jìn)行猜測谱俭,會發(fā)現(xiàn)很多點(diǎn)都有變化的可能,如果對這些變化都進(jìn)行分離和管理宵蛀,確實會提高系統(tǒng)的內(nèi)聚度昆著。但如果我們現(xiàn)在就將整個系統(tǒng)每個可能的變化點(diǎn)都分離出來,無疑會讓整個系統(tǒng)陷入無邊無際的不必要的復(fù)雜度术陶。

破解這類難題的方法是:既然我們知道高內(nèi)聚凑懂,低耦合的設(shè)計是為了軟件更容易應(yīng)對變化的,那么我們?yōu)楹尾环催^來梧宫,讓實際發(fā)生的需求變化來驅(qū)動我們識別變化接谨,管理變化摆碉,從而讓我們的系統(tǒng)達(dá)到恰如其分的內(nèi)聚度和耦合度?

策略一:消除重復(fù)

首先進(jìn)入我們射程的就是重復(fù)代碼。編寫重復(fù)代碼不僅僅會讓有追求的程序員感到乏味脓豪。真正致命的是:“重復(fù)”極度違背高內(nèi)聚巷帝、低耦合原則,從而會大幅提升軟件的長期維護(hù)成本扫夜。

我們之前已經(jīng)討論過楞泼,所謂高內(nèi)聚,指的是關(guān)聯(lián)緊密的事物應(yīng)該被放在一起笤闯。沒有比兩段完全相同的代碼關(guān)聯(lián)更為緊密堕阔。因而重復(fù)代碼意味著低內(nèi)聚

而更為糟糕的是颗味,本質(zhì)重復(fù)的代碼超陆,其實都在表達(dá)(即依賴)同一項知識。如果它們表達(dá)(即依賴)的知識發(fā)生了變化浦马,這些重復(fù)的代碼統(tǒng)統(tǒng)都要修改侥猬。因而, 重復(fù)代碼也意味著高耦合

重復(fù)代碼意味著耦合
重復(fù)代碼意味著耦合

因而捐韩,對于完全重復(fù)的代碼進(jìn)行消除,合二為一鹃锈,會讓系統(tǒng)更加高內(nèi)聚荤胁、低耦合

而更為關(guān)鍵的是:如果兩個模塊之間是部分重復(fù)的屎债,則發(fā)出了一個重要的信號:這兩個模塊都至少存在兩個變化原因仅政,或兩重職責(zé)

如下圖所示盆驹,兩個模塊存在著部分重復(fù)圆丹。站在系統(tǒng)的角度看,它們之間存在著不變的部分(即重復(fù)的部分)躯喇;也存在變化的部分(即差異的部分)辫封。這意味著這兩個模塊都存在兩個變化原因。

變化與不變:多重職責(zé)
變化與不變:多重職責(zé)

對于這一類型的重復(fù)廉丽,比較典型的情況有兩種:調(diào)用型重復(fù)倦微,以及回調(diào)型重復(fù)。它們的命名來源于:在重復(fù)消除后正压,重復(fù)與差異之間的關(guān)系是調(diào)用欣福,還是回調(diào)。

調(diào)用型重復(fù)
調(diào)用型重復(fù)
回調(diào)型重復(fù)
回調(diào)型重復(fù)

由此焦履,我們得到了第一個策略:消除重復(fù)拓劝。

這個策略雏逾,非常明確,極具可操作性:當(dāng)你看到重復(fù)時郑临,盡力消除它栖博。

這個策略,明顯提高系統(tǒng)的內(nèi)聚性牧抵,降低了耦合性笛匙。除此之外,還得到一個重大收益:可重用性。事實上犀变,消除重復(fù)的過程妹孙,正是一個提高系統(tǒng)可重用性的過程。

另外對于回調(diào)型重復(fù)的消除获枝,也是一個提高系統(tǒng)可擴(kuò)展性的過程蠢正。

策略二:分離不同的變化方向

除了重復(fù)代碼外,另外一個驅(qū)動系統(tǒng)朝向高內(nèi)聚方向演進(jìn)的信號是:我們經(jīng)常需要因為同一類原因省店,修改某個模塊嚣崭。而這個模塊的其它部分卻保持不變。

比如懦傍,在之前我們對學(xué)生按照身高從低到高排序的例子中雹舀,如果現(xiàn)在我們需要增加對老師按照身高從低到高排序的需求,我們就知道粗俱,排序?qū)ο?/strong>是一個新的變化方向说榆。于是,我們將代碼重構(gòu)為:

template 
void bulb_sort(T objects[], size_t num_of_objects) 
{
  for(size_t y=0; y < num_of_objects - 1; y++) 
  {
    for(size_t x=1; x < num_of_objects - y; x++) 
    {
      if(objects[x].height > objects[x-1].height) 
      {
         SWAP(objects[x], objects[x-1]);
      }
    } 
  }
}

如果隨后又出現(xiàn)一個新的需求:按照學(xué)生身高從高到低排序(原來為從低到高)寸认。此時我們知道排序規(guī)則也是一個變化的方向签财。因此,我們將這個變化方向也從現(xiàn)有代碼中分離出去偏塞。然后得到:

template 
void bulb_sort(T objects[], size_t num_of_objects) 
{
  for(size_t y=0; y < num_of_objects - 1; y++) 
  {
    for(size_t x=1; x < num_of_objects - y; x++) 
    {
      if(objects[x] > objects[x-1]) 
      {
        SWAP(objects[x], objects[x-1]);
      }
    } 
  }
}

分離不同變化方向唱蒸,目標(biāo)在于提高內(nèi)聚度。因為多個變化方向灸叼,意味著一個模塊存在多重職責(zé)神汹。將不同的變化方向進(jìn)行分離,也意味著各個變化方向職責(zé)的單一化怜姿。

從這個例子可以看出慎冤,此策略的應(yīng)用時機(jī)也非常明確:當(dāng)你發(fā)現(xiàn)需求導(dǎo)致一個變化方向出現(xiàn)時,將其從原有的設(shè)計中分離出去沧卢。


分離新的變化方向
分離新的變化方向

對于變化方向的分離蚁堤,也得到了另外一個我們追求的目標(biāo):可擴(kuò)展性

如果我們足夠細(xì)心,會發(fā)現(xiàn)策略消除重復(fù)分離不同變化方向是兩個高度相似和關(guān)聯(lián)的策略:

它們都是關(guān)注于如何對原有模塊進(jìn)行拆分披诗,以提高系統(tǒng)的內(nèi)聚性撬即。(雖然同時也往往伴隨著耦合度的降低,但這些耦合度的降低都發(fā)生在別處呈队,并未觸及該如何定義API以降低客戶與API之間耦合度)剥槐。

另外,如果兩個模塊有部分代碼是重復(fù)的宪摧,往往意味著不同變化方向粒竖。

盡管如此,我們依然需要兩個不同的策略几于。這是因為:變化方向蕊苗,并不總是以重復(fù)代碼的形式出現(xiàn)的(其典型癥狀是散彈式修改,或者if-else沿彭、switch-case朽砰、模式匹配);盡管其背后往往存在一個以重復(fù)代碼形式表現(xiàn)的等價形式(這也是為何copy-paste-modify如此流行的原因)喉刘。

策略三:縮小依賴范圍

前面兩個策略解決了軟件單元該如何劃分的問題∏迫幔現(xiàn)在我們需要關(guān)注模塊之間的粘合點(diǎn)——即API——的定義問題。

需要強(qiáng)調(diào)的是:兩個模塊之間并不存在耦合睦裳,它們的都共同耦合在API上造锅。因而 API如何定義才能降低耦合度,才是我們應(yīng)該關(guān)注的重點(diǎn)廉邑。

耦合點(diǎn):API
耦合點(diǎn):API

從這幅圖可以看出备绽,對于API定義所帶來的耦合度影響,需要遵循如下原則:

  • 首先鬓催,客戶實現(xiàn)模塊的數(shù)量,會對耦合度產(chǎn)生重大的影響恨锚。它們數(shù)量越多,意味著 API 變更的成本越高猴伶,越需要花更大的精力來仔細(xì)斟酌课舍。
  • 其次办桨,對于影響面大的API(也意味著耦合度高)损姜,需要使用更加彈性的API定義框架饰剥,以有利于向前兼容性。

而具體到策略縮小依賴范圍摧阅,它強(qiáng)調(diào):

  1. API 應(yīng)包含盡可能少的知識汰蓉。因為任何一項知識的變化都會導(dǎo)致雙方的變化;
  2. API 也應(yīng)該高內(nèi)聚,而不應(yīng)該強(qiáng)迫API的客戶依賴它不需要的東西棒卷。

策略四:向著穩(wěn)定的方向依賴

但是顾孽,無論我們?nèi)绾?strong>縮小依賴范圍,如果兩個模塊需要協(xié)作比规,它們之間必然存在耦合點(diǎn)(即API)若厚。降低耦合度的努力似乎已經(jīng)走到了盡頭。

我們知道苞俘,耦合的最大問題在于:耦合點(diǎn)的變化盹沈,會導(dǎo)致依賴方跟著變化。但這也意味著吃谣,如果耦合點(diǎn)從來不會變化乞封,那么依賴方也就不會因此而變化。換句話說岗憋,耦合點(diǎn)越穩(wěn)定肃晚,依賴方受耦合變化影響的概率就越低。

由此仔戈,我們得到最后一個策略:向著穩(wěn)定的方向依賴关串。

那么,究竟什么樣的API更傾向于穩(wěn)定监徘?不難知道晋修,站在What,而不是How的角度凰盔;即
站在需求的角度墓卦,而不是實現(xiàn)方式的角度定義API,會讓其更加穩(wěn)定户敬。

而需求的提出方落剪,一定是客戶端,而不是實現(xiàn)側(cè)尿庐。這就意味著忠怖,我們在定義接口時,應(yīng)該站在客戶的角度抄瑟,思考用戶的本質(zhì)需要凡泣,由此來定義API。而不是站在技術(shù)實現(xiàn)的方便程度角度來思考API定義。

而這正是封裝信息隱藏的關(guān)鍵问麸。

小結(jié)

這四個策略往衷,前兩者聚焦于如何劃分模塊,后兩個聚焦于如何定義模塊間的API严卖。換句話說席舍,前兩者關(guān)注于“如何分”,后兩條聚焦于“怎么合”哮笆。

這四個策略的背后動力非常明確:變化来颤。前兩者,都是在明確的變化方向被第一次識別之后(所謂第一顆子彈)稠肘,進(jìn)行策略運(yùn)用福铅,以讓模塊在變化面前越來越高內(nèi)聚。而后兩者项阴,則是在模塊職責(zé)分離之后滑黔,需要定義模塊間API時,盡可能考慮不同的API定義方式對于依賴雙方的影響环揽,以達(dá)到低耦合略荡。

由于這四個策略致力于讓系統(tǒng)朝著更具正交性的方向演進(jìn),因而它們也被稱做正交策略歉胶,或者正交四原則汛兜。

總結(jié)

本文首先從一個出發(fā)點(diǎn)出發(fā):為了降低軟件復(fù)雜度,提升可重用性通今,我們需要模塊化粥谬。

由此得到了兩個問題:模塊劃分必然要解決如何劃分,以及模塊間如何協(xié)作(API 定義)的問題辫塌。

基于軟件易于應(yīng)對變化的角度出發(fā)漏策。高內(nèi)聚、低耦合原則是最為核心和關(guān)鍵的高層原則臼氨∮寸瑁基于此我們得到了在模塊化過程中,我們真正需要關(guān)注的三方關(guān)系一也。

為了讓高內(nèi)聚、低耦合更具指導(dǎo)性和操作性喉脖,我們提出了四個策略椰苟。它們以變化驅(qū)動,讓系統(tǒng)逐步向更好的正交性演進(jìn)的策略树叽,因此也被稱做正交策略正交原則舆蝴。

我們已經(jīng)在多個系統(tǒng)的設(shè)計和開發(fā)中,以這四個原則來驅(qū)動我們的軟件設(shè)計,不僅讓我們的系統(tǒng)在保持簡單的同時洁仗,具備所有必要的靈活性层皱。也讓設(shè)計和開發(fā)活動變得高度有章可循,讓團(tuán)隊生產(chǎn)率得以大幅提升赠潦。

最后叫胖,推薦劉光聰的文章《實戰(zhàn)正交設(shè)計》。這篇文章通過一個例子她奥,來展示了正交策略是如何驅(qū)動出更加正交的設(shè)計的瓮增。

而正交設(shè)計與SOLID的關(guān)系,可參閱《正交設(shè)計哩俭,OO與SOLID》绷跑。

附錄

而我的朋友及前同事李光磊對此精煉的總結(jié)道:

變化導(dǎo)致的修改有兩類:

  1. 一個變化導(dǎo)致多處修改(重復(fù));
  2. 多個變化導(dǎo)致一處修改(多個變化方向)凡资;
    由此得到前兩個策略:消除重復(fù)砸捏;分離不同變化方向

除此之外隙赁,我們要努力消除變化發(fā)生時不必要的修改垦藏,也有兩種方式:

  1. 不依賴不必要的依賴;
  2. 不依賴不穩(wěn)定的依賴鸳谜;
    這就是后面兩個策略:縮小依賴范圍膝藕,向著穩(wěn)定的方向依賴

從光磊這個精彩總結(jié)中可以清晰的看出:

  1. 一切圍繞著變化:由變化驅(qū)動咐扭,反過來讓系統(tǒng)演進(jìn)的更容易應(yīng)對變化芭挽;
  2. 這四個策略都是在讓系統(tǒng)更加局部化影響
  3. 這四個策略的完備性蝗肪。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末袜爪,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子薛闪,更是在濱河造成了極大的恐慌辛馆,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件豁延,死亡現(xiàn)場離奇詭異昙篙,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)诱咏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門苔可,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人袋狞,你說我怎么就攤上這事焚辅∮澄荩” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵同蜻,是天一觀的道長棚点。 經(jīng)常有香客問我,道長湾蔓,這世上最難降的妖魔是什么瘫析? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮卵蛉,結(jié)果婚禮上颁股,老公的妹妹穿的比我還像新娘。我一直安慰自己傻丝,他們只是感情好甘有,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著葡缰,像睡著了一般亏掀。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上泛释,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天滤愕,我揣著相機(jī)與錄音,去河邊找鬼怜校。 笑死间影,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的茄茁。 我是一名探鬼主播魂贬,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼裙顽!你這毒婦竟也來了付燥?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤愈犹,失蹤者是張志新(化名)和其女友劉穎键科,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體漩怎,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡勋颖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了勋锤。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片饭玲。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖怪得,靈堂內(nèi)的尸體忽然破棺而出咱枉,到底是詐尸還是另有隱情,我是刑警寧澤徒恋,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布蚕断,位于F島的核電站,受9級特大地震影響入挣,放射性物質(zhì)發(fā)生泄漏亿乳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一径筏、第九天 我趴在偏房一處隱蔽的房頂上張望葛假。 院中可真熱鬧,春花似錦滋恬、人聲如沸聊训。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽带斑。三九已至,卻和暖如春勋拟,著一層夾襖步出監(jiān)牢的瞬間勋磕,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工敢靡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留挂滓,地道東北人。 一個月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓啸胧,卻偏偏與公主長得像赶站,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子吓揪,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,071評論 25 707
  • 正交設(shè)計柠辞,是普遍的設(shè)計原則团秽,與粒度無關(guān),與編程范式無關(guān)叭首,更與具體的實現(xiàn)語言無關(guān)习勤。(雖然確實在不同的編程范式下,或使...
    _袁英杰_閱讀 12,310評論 11 66
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理焙格,服務(wù)發(fā)現(xiàn)图毕,斷路器,智...
    卡卡羅2017閱讀 134,651評論 18 139
  • 1.測試與軟件模型 軟件開發(fā)生命周期模型指的是軟件開發(fā)全過程眷唉、活動和任務(wù)的結(jié)構(gòu)性框架予颤。軟件項目的開發(fā)包括:需求囤官、設(shè)...
    宇文臭臭閱讀 6,723評論 5 100
  • 今天的晴陽, 略微顯現(xiàn)出靈魂的干燥蛤虐。 沒有雪党饮, 卻蒼白了臉色和月光。 僅一個夜晚驳庭, 隔了時間刑顺, 偷換了心跳的節(jié)奏。...
    草木縈心閱讀 432評論 3 4