自創(chuàng)實現(xiàn)模式 ~ 以結果驅動代替條件驅動

所屬文章系列:尋找塵封的銀彈:自創(chuàng)實現(xiàn)模式


今天要寫第一篇自創(chuàng)實現(xiàn)模式戈轿,也就是我自己獨家創(chuàng)造的一個實現(xiàn)模式蒜危。

先解釋一下什么是實現(xiàn)模式盼砍,這個詞是Kent Beck《實現(xiàn)模式》的書名碾盟,意思就是在代碼的最底層的模式锄俄,即類內(nèi)部及函數(shù)內(nèi)部的模式奶赠,而設計模式是多個類之間的模式。

說是獨家創(chuàng)造药有,其實很可能早已有人提出來過,或者實踐過愤惰,只是我不知道而已,就像設計模式的最初作者也并非是《設計模式》書中的四個作者一樣宦言,我只是一個模式的總結者而已。無論如何奠旺,對程序員有幫助蜘澜,才是最重要的,也是我的初衷鄙信。

【動機】

先看一段看起來邏輯清晰瞪醋,但有漏洞的代碼:

void DoAdjust(ClassA **a, ClassB **b) {

? ??if ((*a) == NULL)?{

? ? ? ??if ((*b)->next?!= NULL)?{

? ? ? ? ? ??*a = *b;

? ??? ? ? ? *b = (*b)->next;

? ??? ??}

? ??}

}

注:代碼中的雙指針(**)只是為了在函數(shù)體內(nèi)給外部的指針賦值,本質是輸出參數(shù)装诡,方式是解引用银受,即*a。

表面上看起來鸦采,這段代碼邏輯清晰宾巍,但只是看似清晰而已,實際上編寫這段代碼的人并沒有搞清楚這兩個變量在所有執(zhí)行路徑到底該如何賦值赖淤。

這段代碼漏掉了一種情況:當(*a) != NULL時蜀漆,b也應該根據(jù)(*b)->next來決定取值,具體的取值應該有特定的業(yè)務邏輯咱旱。

當我根據(jù)需求編寫了較為完備的測試用例之后确丢,我才發(fā)現(xiàn)了這個Bug。經(jīng)過分析吐限,在這段代碼中鲜侥,確實應該有這種業(yè)務邏輯,但是這段代碼卻使用了默認的“不作為”行為诸典,導致錯誤發(fā)生描函!

于是,我們加上了一些代碼來補上這個漏洞:

void DoAdjust(ClassA **a, ClassB **b) {

? ??if ((*a) == NULL)?{

? ? ? ??if ((*b)->next?!= NULL)?{

? ? ? ? ? ??*a = *b;

? ??? ? ? ? *b = (*b)->next;

? ??? ??}

? ??}?else {

? ? ? ??if ((*b)->next?!= NULL)?{

? ??? ? ? ? *b = (*b)->next;

? ??? ??}

? ??}

}

漏洞是補上了狐粱,但再觀察這段代碼舀寓,總是感覺哪里不對,會不會還有漏洞肌蜻?這是因為我們無法一眼看出問題互墓,必須用白盒的方式枚舉所有的情況。

由于這段代碼比較簡單蒋搜,只有三個條件:*a篡撵、*b、(*b)->next豆挽,需要處理的情況數(shù)= 2(*a為空或非空) * 2(*b為空或非空) * 2((*b)->next為空或非空) = 8,所以枚舉起來并不太難膛檀。

但是宿刮,當條件的數(shù)目猛增到10個時胡桃,它們的變化就會有2的10次方個翠胰,也就是1024個之景,那么枚舉的方式就顯得很傻锻狗,而且容易出錯轻纪。不過叠纷,程序員總是想追求好方法崇众,讓自己在檢查代碼漏洞的時候可以更加“懶惰”一點顷歌。

有沒有一種方法讓這段代碼看上去一目了然衙吩,一眼就能看出有沒有漏洞?

【典型代碼】

答案當然是有澈蚌。用的方法是“以結果驅動代替條件驅動”。

先看修改后的代碼盈电,下一小節(jié)再給你解釋:

void DoAdjust(ClassA **a, ClassB **b) {

? ?//為a賦值

? ??if ((*a) != NULL) {

? ??? ??*a = *a;

? ??} else if ((*b)->next)

?? ?? ??*a = *b;

? ??} else?{

? ??? ??*a = NULL;

? ??}


? ?//為b賦值

? ??if ((*b)->next?!= NULL)?{

? ??? ??*b = *b->next;

? ??} else {

? ??? ??*b = *b;

? ??}

}

【優(yōu)劣對比】

使用該模式后的代碼,看起來有點累贅吸重,但你稍稍看仔細一點嚎幸,就能完全懂得代碼的所有意圖和所有變化,做到了然于心。

為什么這樣說舶担?

這個函數(shù)要解決的根本問題是為*a和*b返回值闸氮,而使用該模式的代碼恰恰是從這個為*a和*b賦值的角度出發(fā)的译断,我們一下子就能看出*a和*b是不是在退出的時候被賦過值了。這就是“以結果驅動代替條件驅動”這個實現(xiàn)模式中的“結果驅動”翎蹈。

反觀未使用該模式的代碼,它是從條件的角度出發(fā),條件包括*a、*b和(*b)->next,我們只看到了眼花繚亂的各種條件組合蕊唐,卻容易忽略掉最終要給每個返回變量賦值。這就是“以結果驅動代替條件驅動”這個實現(xiàn)模式中的“條件驅動”恋谭。

【思維進階(一):單一路徑思維糠睡、顯式思維】

該模式體現(xiàn)出的是兩種思維:單一路徑思維和顯式思維疚颊。

單一路徑思維:函數(shù)必須確保在函數(shù)內(nèi)為每個輸出變量賦值的地方只有一處材义,而不依賴于函數(shù)內(nèi)其他路徑中的賦值代碼。如“典型代碼”小節(jié)中的代碼:

? ?//為a賦值

? ??if ((*a) != NULL) {

? ??? ??*a = *a;

? ??} else if ((*b)->next)

?? ?? ??*a = *b;

? ??} else?{

? ??? ??*a = NULL;

? ??}


顯式思維:在這一處賦值的代碼中油挥,所有if/else路徑都為那個輸出變量賦值款熬,而且必須包含else。代碼示例也是如上這段代碼翩迈。

“單一路徑思維”中還提到了“依賴于函數(shù)內(nèi)其他路徑中的賦值”盔夜,這是反面教材,代碼示例如下:

void DoAdjust(ClassA **a, ClassB **b) {

? ??if (flag == TRUE) {

? ??? ?*a = NULL; //此處為“其他路徑”

? ??}


? ??if ((*a) != NULL) {

? ??? ??*a = *a;

? ??} else if ((*b)->next)

?? ?? ??*a = *b;

? ??} else

? ??? ??*a = NULL;

? ??}

? ??...

}

這種代碼就會讓人思維混亂返十,理不清頭緒洞坑。

【思維進階(二):多個條件的優(yōu)先級】

這個模式會迫使你思考:

到底是在什么特定情況下蝇率,該給什么值本慕。而不是遇到“條件1”先給賦一個值,再遇到“條件2”再給賦一個值锅尘,就像剛舉的這個反面教材代碼那樣。當我們有本文這種模式的思維時浪腐,就會豁然開朗议街,一切邏輯都是那么清晰可見!

再看這種“營養(yǎng)不良”的代碼產(chǎn)生的原因:一般是由于多個人先后修改代碼產(chǎn)生的特漩。后面修改代碼的人不去看已有代碼犹菱,他也不知道兩個條件的優(yōu)先級是什么腊脱,他只是加上自己的邏輯就以為萬事大吉。這種情況在真實的產(chǎn)品代碼里非常常見悍抑,它是一大類Bug的根源,卻又藏得很深搜骡。

【結束語】

當我們了解了本文所講的“以結果驅動代替條件驅動”這種實現(xiàn)模式后记靡,即便不能按照這種模式修改“由堆積如山的條件”組成的產(chǎn)品代碼,也能通過本文的思路找到Bug的根源摸吠,從而找到合適的解決方案。


作于2018-5-23


------------------------------

心定時刻:夫道不欲雜呀洲,雜則多啼止,多則擾献烦,擾則憂,憂而不救贰您。

------------------------------

?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拢操,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子杠园,更是在濱河造成了極大的恐慌舔庶,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件瞧甩,死亡現(xiàn)場離奇詭異肚逸,居然都是意外死亡,警方通過查閱死者的電腦和手機朦促,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門务冕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來幻赚,“玉大人臊旭,你說我怎么就攤上這事油湖》Φ拢” “怎么了吠昭?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵矢棚,是天一觀的道長蒲肋。 經(jīng)常有香客問我,道長兜粘,這世上最難降的妖魔是什么孔轴? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任路鹰,我火速辦了婚禮,結果婚禮上优构,老公的妹妹穿的比我還像新娘雁竞。我一直安慰自己浓领,他們只是感情好,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著泪幌,像睡著了一般署照。 火紅的嫁衣襯著肌膚如雪吗浩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天禁荸,我揣著相機與錄音赶熟,去河邊找鬼陷嘴。 笑死,一個胖子當著我的面吹牛邑退,可吹牛的內(nèi)容都是我干的劳澄。 我是一名探鬼主播浴骂,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼溯警,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了食磕?” 一聲冷哼從身側響起喳挑,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤伊诵,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后搂橙,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體区转,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年侄泽,在試婚紗的時候發(fā)現(xiàn)自己被綠了悼尾。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片诀豁。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖娩践,靈堂內(nèi)的尸體忽然破棺而出翻伺,到底是詐尸還是另有隱情,我是刑警寧澤拉宗,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布旦事,位于F島的核電站,受9級特大地震影響急灭,放射性物質發(fā)生泄漏姐浮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一葬馋、第九天 我趴在偏房一處隱蔽的房頂上張望卖鲤。 院中可真熱鬧,春花似錦畴嘶、人聲如沸蛋逾。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽区匣。三九已至,卻和暖如春蟀瞧,著一層夾襖步出監(jiān)牢的瞬間沉颂,已是汗流浹背条摸。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留钉蒲,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像躬厌,于是被迫代替她去往敵國和親扛施。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

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

  • 1. Java基礎部分 基礎部分的順序:基本語法谍肤,類相關的語法刷钢,內(nèi)部類的語法,繼承相關的語法阱缓,異常的語法,線程的語...
    子非魚_t_閱讀 31,581評論 18 399
  • Lua 5.1 參考手冊 by Roberto Ierusalimschy, Luiz Henrique de F...
    蘇黎九歌閱讀 13,743評論 0 38
  • 第2章 基本語法 2.1 概述 基本句法和變量 語句 JavaScript程序的執(zhí)行單位為行(line)玖媚,也就是一...
    悟名先生閱讀 4,118評論 0 13
  • ¥開啟¥ 【iAPP實現(xiàn)進入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
    小菜c閱讀 6,358評論 0 17
  • 電話不停在吵涩维, 老板不停地鬧辰狡。 總逃不開工作表, 做完了又來了怎樣也甩不掉薄湿。 回家感覺真好, 別管世俗紛擾坐求。 把一...
    賈小呆520閱讀 473評論 0 0