(轉(zhuǎn))重構(gòu) - 改善既有代碼的設(shè)計(jì)

重構(gòu) - 改善既有代碼的設(shè)計(jì)

1 重構(gòu),第一個(gè)示例

  • 重構(gòu)前筝尾,先檢查自己是否有一套可靠的測(cè)試集。這些測(cè)試必須有自我驗(yàn)證能力办桨。TDD
  • 重構(gòu)技術(shù)就是以微小的步伐修改程序筹淫。如果犯下錯(cuò)誤,很容易便可發(fā)現(xiàn)它呢撞。
  • 傻瓜都能寫出計(jì)算機(jī)可以理解的代碼损姜。唯有能寫出人類容易理解的代碼的,才是優(yōu)秀的程序員殊霞。
  • 編程時(shí)薛匪,需要遵循營(yíng)地法則:保證你離開(kāi)時(shí),代碼庫(kù)一定比來(lái)的時(shí)候更健康脓鹃。
  • 好代碼驗(yàn)證的標(biāo)準(zhǔn)是人們是否能輕而易舉的修改它逸尖。

2 重構(gòu)的原則

2.1 何謂重構(gòu)

  • 重構(gòu):對(duì)軟件內(nèi)部結(jié)構(gòu)的一種調(diào)整,目的是在不改變可觀察行為的前提下瘸右,提高其可理解性娇跟,降低其修改成本。
  • 重構(gòu)的關(guān)鍵在于運(yùn)用大量小且保證軟件行為的步驟太颤,一步步達(dá)到大規(guī)模的修改苞俘。
  • 如果有人說(shuō)他們的代碼在重構(gòu)過(guò)程中有1-2天時(shí)間不可用,基本上可以確定龄章,他們?cè)谧龅氖虏皇侵貥?gòu)吃谣。

2.2 兩頂帽子

  • 添加新功能乞封,可能需要優(yōu)化之前的程序結(jié)構(gòu);當(dāng)功能開(kāi)發(fā)好岗憋,也需要優(yōu)化下程序的結(jié)構(gòu)肃晚。不同的角色切換,是在添加功能過(guò)程中必不可少的步驟仔戈。

2.3 為何重構(gòu)

  • 改進(jìn)軟件的設(shè)計(jì)
  • 使軟件更容易理解
  • 幫助開(kāi)發(fā)者找到bug
  • 提高編程速度

2.4 何時(shí)重構(gòu)

  • 預(yù)備性:讓添加新功能更容易
  • 幫助理解:使代碼更易懂
  • 撿垃圾式重構(gòu)
  • 有計(jì)劃和見(jiàn)機(jī)行事的重構(gòu):骯臟的代碼必須重構(gòu)关串,但漂亮的代碼也需要很多重構(gòu)
  • 長(zhǎng)期重構(gòu)
  • 復(fù)審代碼時(shí)重構(gòu)
  • 何時(shí)不應(yīng)該重構(gòu):
    • 只有當(dāng)需要理解其工作原理時(shí)
    • 如果重寫比重構(gòu)容易

2.5 重構(gòu)的挑戰(zhàn)

  • 緩解新功能開(kāi)發(fā)
    • 重構(gòu)的唯一目的就是讓我們開(kāi)發(fā)更快,用更少的工作量創(chuàng)造更大的價(jià)值
    • 重構(gòu)應(yīng)該總是由經(jīng)濟(jì)利益驅(qū)動(dòng)监徘,而不是在于把代碼庫(kù)打磨得閃閃發(fā)光
  • 分支
    • 持續(xù)集成晋修,也叫基于主干開(kāi)發(fā),避免任何分支彼此差距太大凰盔,從而降低合并的難度
  • 測(cè)試
    • 自測(cè)試代碼:快速發(fā)現(xiàn)錯(cuò)誤
  • 遺留代碼
    • 重構(gòu)可以很好地幫助我們理解遺留系統(tǒng)墓卦,但遺留的系統(tǒng)大多數(shù)是沒(méi)有測(cè)試。解決辦法是:沒(méi)測(cè)試就加測(cè)試户敬。書籍推薦《修改代碼的藝術(shù)》

2.6 重構(gòu)落剪、架構(gòu)和YAGNI

  • 一旦代碼寫出來(lái),架構(gòu)就固定了山叮,只會(huì)因?yàn)槌绦騿T的草率對(duì)待而逐漸腐敗,重構(gòu)可以改變這個(gè)狀態(tài)
  • 重構(gòu)可以應(yīng)對(duì)未來(lái)的需求變化

2.7 重構(gòu)與軟件開(kāi)發(fā)過(guò)程

  • 極限編程是最早的敏捷軟件開(kāi)發(fā)方法之一添履。要真正以敏捷的方式運(yùn)作項(xiàng)目屁倔,團(tuán)隊(duì)成員必須在重構(gòu)上有能力、有熱情暮胧,他們采用的開(kāi)發(fā)過(guò)程必須與常規(guī)的锐借、持續(xù)的重構(gòu)相匹配
  • 自測(cè)試代碼 -> 持續(xù)集成 -> 重構(gòu)

2.8 重構(gòu)與性能

  • 除了對(duì)性能有嚴(yán)格要求的實(shí)時(shí)系統(tǒng),其他情況下往衷,“編寫快速軟件”的秘訣是:先寫出可調(diào)優(yōu)的代碼钞翔,然后調(diào)優(yōu)它以求獲得足夠的速度
  • 短期看,重構(gòu)可能會(huì)讓軟件變慢席舍,但它的優(yōu)化階段的軟件性能調(diào)優(yōu)更容易布轿,最終還是會(huì)得到好的效果

2.9 重構(gòu)起源何處

  • 優(yōu)秀的程序員肯定會(huì)花一些時(shí)間來(lái)清理自己的代碼,因?yàn)樗麄兇_定自己幾乎無(wú)法一開(kāi)始就寫出整潔的代碼

2.10 自動(dòng)化重構(gòu)

  • 強(qiáng)大的IDE會(huì)讓重構(gòu)變得很輕松

3 代碼的壞味道

  • 神秘命名:如果想不出一個(gè)好的名字来颤,說(shuō)明背后很可能隱藏著更深的設(shè)計(jì)問(wèn)題
  • 重復(fù)代碼:優(yōu)化:對(duì)比差異汰扭,提取相同。
  • 過(guò)長(zhǎng)函數(shù):優(yōu)化:條件福铅、循環(huán)萝毛、公共集中的過(guò)程提取處理
  • 過(guò)長(zhǎng)參數(shù)列表:優(yōu)化:使用對(duì)象合并參數(shù)
  • 全局?jǐn)?shù)據(jù):優(yōu)化:合并數(shù)據(jù)到方法、類成員中
  • 可變數(shù)據(jù):優(yōu)化:函數(shù)式編程滑黔、數(shù)據(jù)永不改變
  • 發(fā)散式變化:做出的某個(gè)模塊的小修改笆包,必須修改某個(gè)類的多個(gè)函數(shù)环揽。優(yōu)化:每次只關(guān)心一個(gè)上下文,將聯(lián)動(dòng)的改變提取處理
  • 霰彈式修改:每次遇到變化庵佣,都必須在很多不同的類內(nèi)做出許多小修改歉胶。優(yōu)化:提取公共方法
  • 依戀情結(jié):如果一個(gè)函數(shù)跟另一個(gè)模塊中的函數(shù)或者數(shù)據(jù)交流格外頻繁,遠(yuǎn)勝于在自己所處的模塊內(nèi)部交流
    • 模塊化:力求將代碼分出區(qū)域秧了,最大化區(qū)域內(nèi)部的交互跨扮、最小化跨區(qū)域的交互。所謂高內(nèi)聚验毡,低耦合
  • 數(shù)據(jù)泥團(tuán):兩個(gè)類中相同的字段衡创、許多函數(shù)簽名中相同的參數(shù)。優(yōu)化:提取公共字段為一個(gè)類晶通、對(duì)象
  • 基本類型偏執(zhí):一些基本類型無(wú)法表示一個(gè)數(shù)據(jù)的真實(shí)意義璃氢,例如電話號(hào)碼、溫度等狮辽。優(yōu)化:使用類一也、對(duì)象字符串類型變量取代基本類型
  • 重復(fù)的 switch:優(yōu)化:使用策略模式、提取子類
  • 循環(huán)語(yǔ)句:同過(guò)長(zhǎng)函數(shù)
  • 冗贅的元素:優(yōu)化:內(nèi)聯(lián)喉脖、刪除
  • 夸夸其談通用性:優(yōu)化:內(nèi)聯(lián)椰苟、刪除
  • 臨時(shí)字段:優(yōu)化:內(nèi)聯(lián)、刪除
  • 過(guò)長(zhǎng)的消息鏈:優(yōu)化:減少委托關(guān)系
  • 中間人:優(yōu)化:用繼承替代代理委托
  • 內(nèi)幕交易:優(yōu)化:合并相同的聯(lián)系树叽,提取不同的成分
  • 過(guò)大的類:優(yōu)化:提取類舆蝴、子類、接口
  • 異曲同工的類:優(yōu)化:提取公共類题诵、使用子類繼承
  • 純數(shù)據(jù)類:它們擁有一些字段洁仗,以及訪問(wèn)、讀寫這些字段的函數(shù)性锭。優(yōu)化:將相關(guān)操作封裝進(jìn)去赠潦,降低 public 成員變量
  • 被拒絕的遺贈(zèng):如果子類繼承超類的數(shù)據(jù)和方法,但不使用草冈。優(yōu)化:用內(nèi)聯(lián)數(shù)據(jù)和方法她奥、代理委托替代繼承關(guān)系
  • 注釋:當(dāng)你感覺(jué)需要編寫注釋時(shí),請(qǐng)先嘗試重構(gòu)怎棱,試著讓所有注釋變得多余

4 構(gòu)筑測(cè)試體系

4.1 自測(cè)試代碼的價(jià)值

  • 程序員編寫代碼的時(shí)間僅占所有時(shí)間中很少的一部分方淤,但是花費(fèi)在調(diào)試上的時(shí)間是最多的。修復(fù)bug通常是比較快的蹄殃,但找出bug所在卻是一場(chǎng)噩夢(mèng)
  • 確保所有測(cè)試都是完全自動(dòng)化携茂,讓他們檢查自己的測(cè)試結(jié)果
  • 一套測(cè)試就是一個(gè)強(qiáng)大的bug偵探器,能夠大大縮減查找bug所需的時(shí)間

4.2 測(cè)試代碼示例

4.3 第一個(gè)測(cè)試

  • 總是確保測(cè)試不該通過(guò)時(shí)诅岩,會(huì)產(chǎn)生失敗
  • 頻繁地運(yùn)行測(cè)試讳苦,對(duì)于你正在處理的代碼與其對(duì)應(yīng)的測(cè)試至少每隔幾分鐘就要運(yùn)行一次带膜,每天至少運(yùn)行一次所有的測(cè)試

4.4 再添加一個(gè)測(cè)試

  • 編寫為臻完善的測(cè)試并經(jīng)常運(yùn)行,好過(guò)對(duì)完美測(cè)試的無(wú)盡等待
  • 保持每個(gè)測(cè)試用例獨(dú)立性鸳谜,避免產(chǎn)生共享對(duì)象膝藕。因?yàn)闇y(cè)試之間會(huì)通過(guò)共享產(chǎn)生交互,而測(cè)試的結(jié)果就會(huì)受測(cè)試運(yùn)行次序的影響咐扭,導(dǎo)致測(cè)試結(jié)果的不確定性
  • 例子
    describe('province', () => {
        const shanghai = new Province('shanghai');
        it('shortfall', () => {
            expect(shanghai.shortfall).equal(5)
        })
    })
    
    change to
    describe('province', () => {
        let shanghai = null;
        beforeEach(() => {
            shanghai = new Province('shanghai');
        })
        it('shortfall', () => {
            expect(shanghai.shortfall).equal(5)
        })
    })
    

4.5 修改測(cè)試夾具

  • 配置 - 檢查 - 驗(yàn)證
  • 準(zhǔn)備 - 行為 - 斷言

4.6 探測(cè)邊界條件

  • 考慮可能出錯(cuò)的邊界條件芭挽,把測(cè)試火力集中在那兒
  • 不要因?yàn)闇y(cè)試無(wú)法捕捉所有的bug就不寫測(cè)試,因?yàn)闇y(cè)試的確可以捕捉到大多數(shù)bug
  • 任何測(cè)試都不能證明一個(gè)程序沒(méi)有bug
  • 當(dāng)測(cè)試數(shù)量達(dá)到一定程度后蝗肪,繼續(xù)增加測(cè)試代理的邊際效用會(huì)遞減
  • 應(yīng)該把測(cè)試集中在可能出錯(cuò)的地方袜爪,觀察代碼,看哪兒變得復(fù)雜薛闪、哪些地方可能出錯(cuò)

4.7 測(cè)試遠(yuǎn)不止如此

  • 一個(gè)架構(gòu)的好壞辛馆,很大程度上要取決于它的可測(cè)試性,這是一個(gè)好的行業(yè)趨勢(shì)
  • 每當(dāng)收到bug報(bào)告豁延,請(qǐng)先寫一個(gè)單元測(cè)試來(lái)暴露這個(gè)bug
  • 一個(gè)測(cè)試集是否夠好昙篙,最好的衡量標(biāo)準(zhǔn)其實(shí)是主觀的,試問(wèn)自己:如果有人在代碼里引入了一個(gè)缺陷诱咏,自己有多大的自信它能被測(cè)試集發(fā)現(xiàn)

5 介紹重構(gòu)名錄

6 第一組重構(gòu)

6.1 提煉函數(shù)

  • 對(duì)立:內(nèi)聯(lián)函數(shù)

  • 目的:將意圖與實(shí)現(xiàn)分開(kāi)苔可。意圖 == 主干;實(shí)現(xiàn) == 分支的實(shí)現(xiàn)

  • 場(chǎng)景:如果需要花時(shí)間瀏覽一段代碼才能弄清它到底干什么袋狞,那么就應(yīng)該將其提煉到一個(gè)函數(shù)中焚辅,并根據(jù)它所做的事為其命名。以后再讀到這段代碼時(shí)硕并,可以一眼就能知道函數(shù)的用途法焰,大多數(shù)根本不需要關(guān)心函數(shù)如何實(shí)現(xiàn)秧荆。

  • 例子:

    function printOwing(invoice){
        printBanner();
        const outstanding = calculateOutstanding();      
        //print details
        console.info('name:', invoice.name);
        console.info('amount:', outstanding);
    }
    
    function printOwing(invoice){
        printBanner();
        const outstanding = calculateOutstanding();
        printDetails(outstanding, invoice);
    
        function printDetails(){
            console.info('name:', invoice.name);
            console.info('amount:', outstanding);
        }
    }
    

6.2 內(nèi)聯(lián)函數(shù)

  • 對(duì)立:提煉函數(shù)

  • 目的:去除不必要間接層/委托層倔毙,降低系統(tǒng)復(fù)雜度

  • 場(chǎng)景:

    • 一堆不合理的函數(shù),可以將其內(nèi)聯(lián)到一個(gè)大型函數(shù)中乙濒,再重新提煉到小函數(shù)中
    • 代碼太多間接層陕赃,使系統(tǒng)中的所有函數(shù)都似乎只是對(duì)另一個(gè)函數(shù)簡(jiǎn)單的委托
  • 例子:

    function getRating(driver){
        return moreThanFiveDeliveries(driver) ? 2 : 1;
    }
    
    function moreThanFiveDeliveries(driver){
        return driver.deliveries > 5;
    }
    
    function getRating(driver){
        return driver.deliveries > 5 ? 2 : 1;
    }
    

6.3 提煉變量

  • 對(duì)立:內(nèi)聯(lián)變量

  • 目的:將復(fù)雜的表達(dá)式使用變量說(shuō)明

  • 例子:

    return order.quantity * order.itemPrice - Math.max(0, order.quantity - 500) * order.itemPrice * 0.05 + Math.min(order.quantity * order.itemPrice * 0.1, 100);
    
    const basePrice = order.quantity * order.itemPrice;
    const quantityDiscount =  Math.max(0, order.quantity - 500) * order.itemPrice * 0.05;
    const shipping = Math.min(basePrice * 0.1, 100);
    return basePrice - quantityDiscount + shipping;
    

6.4 內(nèi)聯(lián)變量

  • 對(duì)立:提煉變量

  • 目的:去除不必要變量

  • 場(chǎng)景:

    • 表達(dá)式比變量更有表現(xiàn)力
  • 例子:

    const basePrice = order.basePrice;
    return basePrice > 25;
    
    return order.basePrice > 25;
    

6.5 改變函數(shù)聲明

  • 目的:好名字能讓人一眼看出函數(shù)的用途,而不必看代碼實(shí)現(xiàn)

  • 使用:

    • 先寫一句注釋描述這個(gè)函數(shù)的用途颁股,再把這句注釋變成函數(shù)的名字
  • 例子:

    function calc(){}
    
    function calcOrder(){}
    

6.6 封裝變量

  • 目的:

    • 重構(gòu)數(shù)據(jù)轉(zhuǎn)移為重構(gòu)函數(shù)么库,更易于處理
    • 監(jiān)控?cái)?shù)據(jù)的變化
  • 場(chǎng)景:如果數(shù)據(jù)的可訪問(wèn)范圍大

  • 例子:

    let defaultOwner = {};
    
    let defaultOwner = {};
    export function defaultOwner(){
        return defaultOwner;
    }
    export function getDefaultOwner(arg){
        defaultOwner = arg;
    }
    

6.7 變量改名

  • 目的:好名字可讓上下文更清晰

  • 例子:

    const a = height * width;
    
    const area = height * width;
    

6.8 引入?yún)?shù)對(duì)象

  • 目的:組織數(shù)據(jù)結(jié)構(gòu),讓數(shù)據(jù)項(xiàng)之間的關(guān)系更清晰甘有,參數(shù)列表也能縮短

  • 場(chǎng)景:一個(gè)函數(shù)接受多個(gè)參數(shù)

  • 例子:

    function invoice(startDate, endDate){}
    function received(starDate, endDate){}
    
    function invoice(dateRange){}
    function received(dateRanges){}
    

6.9 函數(shù)組合成類

  • 目的:

    • 對(duì)象內(nèi)部調(diào)用這些函數(shù)可以少傳參數(shù)诉儒,從而簡(jiǎn)化函數(shù)調(diào)用,而且一個(gè)對(duì)象可更方便傳遞給系統(tǒng)的其他部分
    • 客戶端修改對(duì)象的核心數(shù)據(jù)亏掀,通過(guò)計(jì)算得出的派生數(shù)據(jù)會(huì)自動(dòng)與核心數(shù)據(jù)保存一致
  • 場(chǎng)景:如果一組函數(shù)形影不離地操作同一塊數(shù)據(jù)(通常是將這塊數(shù)據(jù)作為參數(shù)傳遞給函數(shù))

  • 例子:

    function base(reading){}
    function taxableCharge(reading){}
    function calcBaseCharge(reading){}
    
    class Reading{
        base(){}
        taxableCharge(){}
        calcBaseCharge(){}
    }
    

6.10 函數(shù)組合成變換

  • 目的:

    • 增強(qiáng)數(shù)據(jù)忱反,將數(shù)據(jù)邏輯統(tǒng)一在一個(gè)地方處理
    • 高內(nèi)聚
  • 場(chǎng)景:需要把數(shù)據(jù)放到另一個(gè)程序中運(yùn)行泛释,計(jì)算出各種派生信息

  • 例子:

    function base(reading){}
    function taxableCharge(reading){}
    
    function enrichReading(arg){
        const reading = _.cloneDeep(arg);
        reading.baseCharge = base(reading);
        reading.taxableCharge = taxableCharge(reading);
        return reading;
    }
    

6.11 拆分階段

  • 目的:保證單一原則,一段代碼只做一件事

  • 場(chǎng)景:如果一段代碼同時(shí)處理兩件或者更多不同的事情

  • 例子:

    const orderArr = orderStr.split(/\s+/);
    const productPrice = priceList(order[0].split('-')[1]);
    const orderPrice = parseInt(orderArr[1]) * productPrice;
    
    const orderRecord = parseOrder(order);
    const orderPrice = price(orderRecord, priceList);
    
    function parseOrder(str){
        const values = str.split(/\s+/);
        return {
            priceId: values[0].split('-')[1],
            quantity: parseInt(values[1]) 
        };
    }
    
    function price(order, priceList){
        return order.quantity * priceList[order.productId];
    }
    

7 封裝

7.1 封裝記錄

  • 目的:

    • 對(duì)象可以隱藏結(jié)構(gòu)的細(xì)節(jié)
    • 有助于字段改名
  • 場(chǎng)景:對(duì)于可變數(shù)據(jù)

  • 例子:

    const organization = {name: 'John', country: 'GB'};
    
    class Organization{
      constructor(data){
        this._name = data.name;
        this._country = data.country;
      }
      get name(){
        return this._name;
      }
      set name(arg){
        this._name = arg;
      }
      get country(){
        return this._country;
      }
      set country(arg){
        this._country = arg;
      }
    }
    

7.2 封裝集合

  • 目的:控制外界對(duì)類中集合的訪問(wèn)權(quán)温算,避免集合被直接修改

  • 場(chǎng)景:類中集合為可變數(shù)據(jù)

  • 例子:

    class Person{
      get courses(){
        return this._courses;
      }
      set courses(list){
        this._course = list;
      }
    }
    
    class Person{
      get course(){
        return this._courses.slice();
      }
      addCourse(course){}
      removeCourse(course){}
    }
    

7.3 以對(duì)象取代基本類型

  • 目的:擴(kuò)展或者增強(qiáng)數(shù)據(jù)的行為

  • 場(chǎng)景:如果數(shù)據(jù)項(xiàng)需要更多的含義或者行為時(shí)

  • 例子:

    orders.filter(o => 'high' === o.priority || 'rush' === o.priority);
    
    orders.filter(o => o.priority.higherThan(new Priority('normal')));
    

7.4 以查詢?nèi)〈R時(shí)變量

  • 目的:

    • 新函數(shù)與原函數(shù)之間的邊界更清晰
    • 便于代碼抽離
    • 利于函數(shù)復(fù)用
  • 場(chǎng)景:那些值被計(jì)算一次且之后不再被修改的變量

  • 例子:

    const basePrice = this._quantity * this._itemPrice;
    if(basePrice > 1000){
      return basePrice * 0.95;
    }else{
      return basePrice * 0.98;
    }
    
    get basePrice(){ 
      this._quantity * this._itemPrice;
    }
    if(this.basePrice > 1000){
      return this.basePrice * 0.95;
    }else{
      return this.basePrice * 0.98;
    }
    

7.5 提取類

  • 對(duì)立:內(nèi)聯(lián)類

  • 目的:將大類分成小類

  • 場(chǎng)景:如果維護(hù)一個(gè)大量函數(shù)和數(shù)據(jù)的類

  • 例子:

    class Person{
        get officeAreaCode(){
            return this._officeAreaCode;
        }
        get officeNumber(){
            return this._officeNumber;
        }
    }
    
    class Person{
        get officeAreaCode(){
            return this._telephoneNumber.areaCode;
        }
        get officeNumber(){
            return this._telephoneNumber.number;
        }
    }
    class TelephoneNumber{
      get areaCode(){
        return this._areaCode;
      }
      get number(){
        return this._number;
      }
    }
    

7.6 內(nèi)聯(lián)類

  • 對(duì)立:提煉類

  • 目的:減少不必要的類

  • 場(chǎng)景:

    • 如果一個(gè)類不再承擔(dān)足夠的責(zé)任
    • 重新分類兩個(gè)類的不同職責(zé)
  • 例子:

    class Person{
        get officeAreaCode(){
            return this._telephoneNumber.areaCode;
        }
        get officeNumber(){
            return this._telephoneNumber.number;
        }
    }
    class TelephoneNumber{
      get areaCode(){
        return this._areaCode;
      }
      get number(){
        return this._number;
      }
    }
    
    class Person{
        get officeAreaCode(){
            return this._officeAreaCode;
        }
        get officeNumber(){
            return this._officeNumber;
        }
    }
    

7.7 隱藏委托關(guān)系

  • 對(duì)立:移除中間人

  • 目的:每個(gè)模塊盡可能減少了解系統(tǒng)的其他部分怜校,把部分依賴關(guān)系隱藏起來(lái),減少調(diào)用者雙方了解更多細(xì)節(jié)

  • 場(chǎng)景:如果被調(diào)方接口頻繁修改時(shí)

  • 例子:

    const manager = person.department.manager;
    
    const manager = person.manager;
    class Person{
      get manager(){
        return this.department.manager;
      }
    }
    

7.8 移除中間人

  • 對(duì)立:隱藏委托關(guān)系

  • 目的:減少不不必要的委托

  • 場(chǎng)景:如果過(guò)多的轉(zhuǎn)發(fā)函數(shù)沒(méi)有讓程序本身提升的擴(kuò)展性注竿,就應(yīng)刪除部分委托

  • 例子:

    const manager = person.manager;
    class Person{
      get manager(){
        return this.department.manager;
      }
    }
    
    const manager = person.department.manager;
    

7.9 替換算法

  • 目的:用簡(jiǎn)單算法處理

  • 場(chǎng)景:隨著對(duì)業(yè)務(wù)不斷深入茄茁,發(fā)覺(jué)有更簡(jiǎn)單的算法實(shí)現(xiàn)

  • 例子:

    function foundPerson(people){
      for(let i = 0; i < people.length; i++){
        if(people[i] === 'John'){
          return 'John';
        }
        if(people[i] === 'Maria'){
          return 'Maria';
        }
         if(people[i] === 'Mike'){
          return 'Mike';
        }
      }
      return '';
    }
    
    function foundPerson(people){
        const candidate = ['John', 'Maria', 'Mike'];
      return people.find(p => candidate.includes(p)) || '';
    }
    

8 搬移特性

8.1 搬移函數(shù)

  • 目的:減少對(duì)不常用函數(shù)的外部依賴,增加常用函數(shù)的內(nèi)部依賴巩割,高內(nèi)聚

  • 場(chǎng)景:如果一個(gè)方法頻繁調(diào)用別處的一個(gè)函數(shù)裙顽,并且被頻繁調(diào)用的函數(shù)在該上下文關(guān)系不大時(shí)

  • 例子:

    class Account{
        get overdraftCharge(){}
    }
    
    class AccountType{
        get overdraftCharge(){}
    }
    

    將內(nèi)聚放在一個(gè)類中,改名喂分。

    變量為名詞

    方法為動(dòng)詞

8.2 搬移字段

  • 目的:高內(nèi)聚锦庸,將不屬于當(dāng)前類的屬性搬到另一個(gè)類中

  • 場(chǎng)景:如果修改一條記錄時(shí),總是需要同時(shí)改動(dòng)另一個(gè)記錄

  • 例子:

    class Customer{
      get plan(){
          return this._plan;
      }
      get disountRate(){
          return this._discountRate;
      }
    }
    
    class Customer{
      get plan(){
          return this._plan;
      }
      get disountRate(){
          return this.plan.discountRate;
      }
    }
    

8.3 搬移語(yǔ)句到函數(shù)

  • 對(duì)立:搬移語(yǔ)句到調(diào)用者

  • 目的:消除重復(fù)蒲祈,提取公共內(nèi)容甘萧,即表現(xiàn)一致的行為

  • 場(chǎng)景:發(fā)現(xiàn)調(diào)用某個(gè)函數(shù)時(shí),總有一些相同的代碼也需要每次執(zhí)行

  • 例子:

    result.push(`<p>title: ${person.photo.title}</p>`);
    result.concat(photoData(person.photo));
    function photoData(photo){
      return {
        `<p>location: ${photo.location}</p>`;
        `<p>date: ${photo.date}</p>`;
      }
    }
    
    result.concat(photoData(person.photo));
    function photoData(photo){
      return {
        `<p>title: ${person.photo.title}</p>`;
        `<p>location: ${photo.location}</p>`;
        `<p>date: ${photo.date}</p>`;
      }
    }
    

8.4 搬移語(yǔ)句到調(diào)用者

  • 對(duì)立:搬移語(yǔ)句到函數(shù)

  • 目的:將可變的行為搬移到調(diào)用者內(nèi)部

  • 場(chǎng)景:以往多個(gè)地方公共的行為梆掸,如今需要在某些調(diào)用點(diǎn)表現(xiàn)不同的行為扬卷,并且調(diào)用點(diǎn)與調(diào)用者之間的邊界差別不大。如果差別較大的酸钦,只能重新設(shè)計(jì)

  • 例子:

    emitPhotoData(outStream, person.photo);
    function emitPhotoData(outStream, photo){
      outStream.write(`<p>title: ${photo.title}</p>`);
      outStream.write(`<p>title: ${photo.location}</p>`);
    }
    
    emitPhotoData(outStream, person.photo);
    outStream.write(`<p>title: ${photo.title}</p>`);
    function emitPhotoData(outStream, photo){
      outStream.write(`<p>title: ${photo.location}</p>`);
    }
    

8.5 以函數(shù)調(diào)用取代內(nèi)聯(lián)代碼

  • 目的:消除重復(fù)

  • 場(chǎng)景:如果一些內(nèi)聯(lián)代碼做的事情是已有函數(shù)可以做到的

  • 例子:

    let hasMa = false;
    for(const i of states){
      if(i === 'MA'){
        hasMa = true;
      }
    }
    
    const hasMa = states.includes('MA');
    

8.6 移動(dòng)語(yǔ)句

  • 目的:高內(nèi)聚內(nèi)部代碼

  • 場(chǎng)景:如果有幾行代碼取用了同一個(gè)數(shù)據(jù)結(jié)構(gòu)怪得,那么最好讓它們?cè)谝黄?/p>

  • 例子:

    const pricePlan = retrievePricingPlan();
    const order = retreiveOrder();
    let charge;
    const chargePerUnit = pricingPlan.unit();
    
    const pricePlan = retrievePricingPlan();
    const chargePerUnit = pricingPlan.unit();
    const order = retreiveOrder();
    let charge;
    

8.7 拆分循環(huán)

  • 目的:保持循環(huán)內(nèi)部只做一件事

  • 場(chǎng)景:如果循環(huán)內(nèi)部身兼多職

  • 例子:

    let averageAge = 0;
    let totalSalary = 0;
    for(const p of people){
      averageAge += p.age;
      totalSalary += p.salary;
    }
    averageAge = averageAge / people.length;
    
    let averageAge = 0;
    for(const p of people){
      averageAge += p.age;
    }
    let totalSalary = 0;
    for(const p of people){
      averageAge += p.age;
    }
    averageAge = averageAge / people.length;
    

    先進(jìn)行重構(gòu),在進(jìn)行性能優(yōu)化卑硫。將代碼變得清晰徒恋,對(duì)后期的擴(kuò)展、優(yōu)化欢伏,都極其方便

8.8 以管道取代循環(huán)

  • 目的:提高代碼可讀性

  • 場(chǎng)景:如果代碼的邏輯可以通過(guò)內(nèi)置方法處理

  • 例子:

    const names = [];
    for(const i of input){
      if(i.job === 'programmer'){
        names.push(i.name);
      }
    }
    
    const names = input
    .filter(i => i.job === 'programmer')
    .map(i => i.name);
    

8.9 移除死代碼

  • 目的:移除無(wú)用代碼

9 重新組織數(shù)據(jù)

9.1 拆分代碼

  • 目的:每個(gè)變量只承擔(dān)一個(gè)責(zé)任入挣,同一個(gè)變量承擔(dān)兩件不同的事情,會(huì)令代碼閱讀者糊涂

  • 場(chǎng)景:大多數(shù)情況下變量只賦值一次硝拧,除了:循環(huán)變量(例如:for(let i =0; i < 5; i++) 中的i)径筏,收集結(jié)果變量

  • 例子:

    let temp = 2 * (height + width);
    temp = height * width;
    
    const perimeter = 2 * (height * width);
    const area = height * width;
    

    變量聲明可以剛開(kāi)始聲明為 const,如果發(fā)覺(jué)需要重復(fù)賦值障陶,再改為 let

9.2 字段改名

  • 目的:好的名字可以幫助閱讀者更易理解

  • 例子:

    class Organization{
        get name(){}
    }
    
    class Organization{
        get title(){}
    }
    

9.3 以查詢?nèi)〈缮兞?/h3>
  • 目的:減少方法中的副作用滋恬,單一原則

  • 場(chǎng)景:對(duì)數(shù)據(jù)的修改常常導(dǎo)致代碼的各個(gè)部分以丑陋的形式互相耦合:在一處修改數(shù)據(jù),卻在另一處造成難以發(fā)現(xiàn)的破壞

  • 例子:

    get discountTotal(){
        return this._discountTotal;
    }
    set discount(number){
        const old = this._discount;
        this._discount = number;
        this._discountTotal += old - number;
    }
    
    get discountTotal(){
        return this._baseTotal - this._discount;
    }
        get discount(){
            return this._discount;
        }
    set discount(number){
        this._discount = number;
    }
    

9.4 將引用對(duì)象改為值對(duì)象

  • 對(duì)立:將值對(duì)象改為引用對(duì)象

  • 目的:值對(duì)象的不可變性處理起來(lái)更容易抱究,可以任意的傳遞恢氯,防止被外部修改

  • 場(chǎng)景:如果不需要改變值的引用關(guān)系,每個(gè)值是不可變的

  • 例子:

    class Product{
        applyDiscount(arg){
            this._price -= arg;
        }
    }
    
    class Product{
        applyDiscount(arg){
            this._price = new Money(this._price.amount - arg, this._price.currency);
        }
    }
    

9.5 將值對(duì)象改為引用對(duì)象

  • 目的:保持?jǐn)?shù)據(jù)共享

  • 場(chǎng)景:如果數(shù)據(jù)結(jié)構(gòu)中包含多個(gè)記錄,而這些記錄都有關(guān)聯(lián)到同一個(gè)邏輯的數(shù)據(jù)結(jié)構(gòu)勋拟,例如一個(gè)數(shù)據(jù)的改動(dòng)遏暴,需要共享到整個(gè)數(shù)據(jù)集

  • 例子:

    let customer = new Customer(customerData);
    
    let customer = customerRepository.get(customerData, id);
    

10 簡(jiǎn)化條件邏輯

10.1 分解條件表達(dá)式

  • 目的:簡(jiǎn)化條件,易于理解代碼邏輯

  • 場(chǎng)景:當(dāng)檢查處理邏輯復(fù)雜時(shí)

  • 例子:

    if(!date.isBefore(plan.summerStart) && !date.isAfter(plan.summerEnd)){
        charge = quantity * plan.summerRate;
    }else{
        charge = quantity * plan.regularRate + plan.regularServiceCharge;
    }
    
    const isSummer = !date.isBefore(plan.summerStart) && !date.isAfter(plan.summerEnd);
    function summerCharge(){
        return quantity * plan.summerRate;
    }
    function regularCharge(){
        return quantity * plan.regularRate + plan.regularServiceCharge;
    }
    
    charge = isSummer() ? summerCharge() : regularCharge();
    

10.2 合并條件表達(dá)式

  • 目的:統(tǒng)一處理?xiàng)l件語(yǔ)句

  • 場(chǎng)景:當(dāng)檢查條件各不相同指黎,最終行為一致時(shí)

  • 例子:

    if(age < 18) return 'younger';
    if(experience < 5) return 'younger';
    if(!isPassTest) return 'younger';
    
    if(isYounger()) return 'younger';
    function isYounger(){
        return age<18 || experience < 5 || !isPassTest;
    }
    

10.3 衛(wèi)語(yǔ)句取代嵌套條件表達(dá)式

  • 目的:減少邏輯的復(fù)雜度

  • 場(chǎng)景:當(dāng)出現(xiàn)需要單獨(dú)檢查某個(gè)特定條件時(shí)

  • 例子:

    function getPayment(){
        let result;
        if(isRead){
            result = deadAmount();
        }else{
            if(isSeparated){
                result = separatedAmount();
            }else{
                if(isRetired){
                    result = retiredAmount();
                }else{
                    result = normalAmount();
                }
            }
        }
    }
    
    function getPayment(){
        if(isRead) return deadAmount();
        if(isSeparated) return separatedAmount();
        if(isRetired) return retiredAmount();
        
        return normalAmount();
    }
    

10.4 以多態(tài)取代條件表達(dá)式

  • 目的:增強(qiáng)擴(kuò)展性朋凉,減少邏輯的復(fù)雜度

  • 場(chǎng)景:當(dāng)多個(gè)邏輯處理情況

  • 例子:

    switch(bird.type){
        case 'EuropeanSwallow':
            return 'EuropeanSwallow';
        case 'AfricanSwallow':
            return 'AfricanSwallow';
        default:
            return 'unknown';
    }
    
    class EuropeanSwallow{
        get name(){
            return 'EuropeanSwallow';
        }
    }
    class AfricanSwallow{
        get name(){
            return 'AfricanSwallow';
        }
    }
    

10.5 引入特例

  • 目的:提供復(fù)用性以及統(tǒng)一性

  • 場(chǎng)景:如果某部分邏輯都在檢查某個(gè)特殊值,并且處理的邏輯也都相同

  • 例子:

    if(customer === 'unknown'){
        customerName = 'occupant';
    }
    
    class UnknownCustomer{
        get name(){
            return 'occupant';
        }
    }
    

10.6 引入斷言

  • 目的:保障傳入值是可預(yù)測(cè)的醋安,預(yù)習(xí)發(fā)現(xiàn)測(cè)試的BUG

  • 例子:

    if(this.discountRate){
        base = base - this.discountRate * base;
    }
    
    asset(this.discountRate>0);
    if(this.discountRate){
        base = base - this.discountRate * base;
    }
    

11 重構(gòu)API

11.1 查詢函數(shù)和修改函數(shù)分離

  • 目的:減少函數(shù)副作用 == 任何有返回值的函數(shù)杂彭,都要減少它的副作用

  • 場(chǎng)景:當(dāng)函數(shù)中又有查詢,又有命令

  • 例子:

    function getTotalOutStandingAndSendBill(person){
        const result = customer.invoice.reduce((total, each)=>each.amount + total, 0);
        sendBill();
        return result;
    }
    
    function getTotalStanding(person){
        return customer.invoice.reduce((total, each)=>each.amount + total, 0);
    }
    function sendBill(){}
    function handler(){
        const totalOutstanding = getTotalStanding();
        sendBill();
    }
    

11.2 函數(shù)參數(shù)化

  • 目的:增強(qiáng)函數(shù)的功能

  • 場(chǎng)景:如果發(fā)現(xiàn)多個(gè)函數(shù)邏輯相似吓揪,只有某1-2個(gè)字面量不同

  • 例子:

    function tenPercentRaise(person){
        person.salary = person.salary.multiply(1.1);
    }
    function fivePercentRaise(person){
        person.salary = person.salary.multiply(0.05);
    }
    
    function raise(person, factor){
        person.salary = person.salary.multiply(factor);
    }
    

11.3 移除標(biāo)記參數(shù)

  • 目的:代碼更清晰亲怠,減少函數(shù)的復(fù)雜度

  • 場(chǎng)景:如果參數(shù)值影響函數(shù)內(nèi)部的控制流

  • 例子:

    function setDimension(name, value){
        if(name === 'height'){}
        if(name === 'width'){}
    }
    
    function setHeight(value){
        this._height = value;
    }
    function setWidth(value){
        this._width = value;
    }
    

11.4 保持對(duì)象完整性

  • 目的:縮短參數(shù)列表,參數(shù)配置靈活

  • 場(chǎng)景:如果傳入多個(gè)參數(shù)傳值

  • 例子:

    const{low, high} = temperature;
    if(isValidTemperature(low, high)){};
    
    if(isValidTemperature(temperature)){};
    

11.5 以查詢?nèi)〈鷧?shù)

  • 對(duì)立:以參數(shù)取代查詢

  • 目的:減少傳參柠辞,從而減少調(diào)用者的成本

  • 場(chǎng)景:如果傳入多個(gè)參數(shù)团秽,并且從一個(gè)參數(shù)推導(dǎo)出另一個(gè)參數(shù)

  • 例子:

    availableVacation(employee, employee.grade);
    function availableVacation(employee, grade){}
    
    availableVacation(employee);
    function availableVacation(employee){
        const {grade} = employee;
    }
    

11.6 以參數(shù)取代查詢

  • 對(duì)立:以查詢?nèi)〈鷧?shù)

  • 目的:減少函數(shù)的副作用,以及引用關(guān)系叭首,保持函數(shù)的純凈度

  • 場(chǎng)景:如果函數(shù)引用了一個(gè)全局變量习勤,或者引用想移除的元素

  • 例子:

    const weather ={};
    targetTemperature(plan);
    
    function targetTemperature(plan){
        const{curTemperature} = weather;
    }
    
    const weather ={};
    targetTemperature(plan, weather);
    
    function targetTemperature(plan, weather){
        const{curTemperature} = weather;
    }
    

11.7 移除設(shè)置函數(shù)

  • 目的:防止某字段被修改

  • 場(chǎng)景:當(dāng)不希望某個(gè)字段被修改時(shí)

  • 例子:

    class Person{
        get id(){}
        set id(name){}
    }
    
    class Person{
        get id(){}
    }
    

11.8 以工廠函數(shù)取代構(gòu)造函數(shù)

  • 目的:增加靈活性

  • 場(chǎng)景:不存在繼承關(guān)系時(shí)

  • 例子:

    const leadEngineer = new Employee('name','E');
    
    const leadEngineer = createEngineer('name');
    

11.9 以命令取代函數(shù)

  • 對(duì)立:以函數(shù)取代命令

  • 目的:命令對(duì)象提供更大的靈活性,并且還可以支持撤銷焙格、生命周期的管理等附加操作

  • 場(chǎng)景:當(dāng)普通函數(shù)無(wú)法提供強(qiáng)有力靈活性

  • 例子:

    function score(candidate, media){
        let result = 0;
        let healthLevel = 0;
    }
    function hasPassMedicalExam(){}
    
    class Scorer{
        constructor(candidate, medicalExam){
            this._candidate = candidate;
            this._medicalExam = medicalExam;
        }
        execute(){
            let result = 0;
            let healthLevel = 0;
        }
        hasPassMedicalExam(){}
    }
    

11.10 以函數(shù)取代命令

  • 對(duì)立:以命令取代函數(shù)

  • 目的:函數(shù)簡(jiǎn)單化

  • 場(chǎng)景:大多數(shù)情況下图毕,只想調(diào)用一個(gè)函數(shù),完成自己的工作眷唉,不需要函數(shù)那么復(fù)雜

  • 例子:

    class ChargeCalculator{
        constructor(customer, usage){
            this._customer = customer;
            this._usage = usage;
        }
        execute(){ 
            return this._customer.rate * this._usage;
        }
    }
    
    function charge(customer, usage){
        return customer.rate * usage;
    }
    

12 處理繼承關(guān)系

繼承體系里上下調(diào)整:函數(shù)上移予颤、字段上移構(gòu)造函數(shù)本體上移冬阳、函數(shù)下移蛤虐、字段下移

繼承體系添加新類或者刪除舊類:移除子類提取超類肝陪、折疊繼承體系

一個(gè)字段僅用于類型碼使用:以子類取代類型碼

如果本來(lái)使用集成的場(chǎng)景變得不再適合:以委托取代子類驳庭、以委托取代超類

12.1 函數(shù)上移

  • 對(duì)立:函數(shù)下移

  • 目的:提高復(fù)用性,減少重復(fù)

  • 場(chǎng)景:當(dāng)函數(shù)被大部分部分子類用到時(shí)

  • 例子:

    class Employee{}
    class Salesman extends Employee{
        get name(){}
    }
    class Engineer extends Employee{
        get name(){}
    }
    
    class Employee{
        get name(){}
    }
    class Salesman extends Employee{}
    class Engineer extends Employee{}
    

12.2 字段上移

移構(gòu)造函數(shù)本體上移

12.3 構(gòu)造函數(shù)本體上移

  • 目的:提高復(fù)用性

  • 場(chǎng)景:當(dāng)多個(gè)子類有公共字段

  • 例子:

    class Party{}
    class Employee extends Party{
        constructor(id){
            this._id = id;
        }
    }
    class Salesman extends Employee{
        constructor(id){
            this._id = id;
            this._name = name;
        }
    }
    
    class Party{
        constructor(id){
            this._id = id;
        }
    }
    class Employee extends Party{
        constructor(id){
            super(id);
        }
    }
    class Salesman extends Employee{
        constructor(id){
            super(id);
            this._name = name;
        }
    }
    

12.4 函數(shù)下移

  • 對(duì)立:函數(shù)上移

  • 目的:內(nèi)聚子類的方法

  • 場(chǎng)景:當(dāng)函數(shù)被小部分子類用到時(shí)

  • 例子:

    class Employee{
        get quota(){}
    }
    class Salesman extends Employee{}
    class Engineer extends Employee{}
    
    class Employee{}
    class Salesman extends Employee{
        get quota(){}
    }
    class Engineer extends Employee{}
    

12.5 字段下移

  • 對(duì)立:字段上移

  • 目的:內(nèi)聚子類的字段

  • 場(chǎng)景:當(dāng)字段被小部分子類用到時(shí)

  • 例子:

    class Employee{
        constructor(quote){
            this._quote = quote;
        }
    }
    class Salesman extends Employee{
        constructor(quote){
            super(quote);
        }
    }
    class Engineer extends Employee{}
    
    class Employee{}
    class Salesman extends Employee{
        constructor(quote){
            this._quote = quote;
        }
    }
    class Engineer extends Employee{}
    

12.6 以子類取代類型碼

  • 對(duì)立:移除子類

  • 目的:更明確地表達(dá)數(shù)據(jù)與類型之間的關(guān)系见坑,增強(qiáng)子類的擴(kuò)展性

  • 場(chǎng)景:當(dāng)不同的狀態(tài)碼表現(xiàn)不同的行為時(shí)

  • 例子:

    function createEmployee(name, type){
        return new Employee(name, type);
    }
    
    function createEmployee(name, type){
        const employeeTypes = (name) =>{
            return{
                'engineer': new Engineer(name),
                'salesman': new Salesman(name)
            }
        }
        return employeeTypes(name)[type];
    }   
    

12.7 移除子類

  • 對(duì)立:以子類取代類型碼

  • 目的:減少系統(tǒng)復(fù)雜度

  • 場(chǎng)景:當(dāng)子類的用處太少時(shí)嚷掠,當(dāng)子類簡(jiǎn)單

  • 例子:

    class Person{
        get genderCode(){
            return 'X';
        }
    }
    class Male extends Person{
        get genderCode(){
            return 'M';
        }
    }
    class Female extends Person {
        get genderCode(){
            return 'F';
        }
    }
    
    class Person{
        constructor(genderCode){
            this._genderCode = genderCode;
        }
        get genderCode(){
            return this._genderCode;
        }
    }
    

12.8 提取超類

  • 目的:把重復(fù)的行為收攏起來(lái)

  • 場(chǎng)景:當(dāng)多個(gè)子類的方法基本一致時(shí)

  • 例子:

    class Department{
        get totalAnnualCost(){}
        get name(){}
    }
    class Employee{
        get annualCost(){}
        get name(){}
        get id(){}
    }
    
    class Party{
        get totalAnnualCost(){}
        get name(){}
    }
    class Department extends Party{
        get primaryCost(){}
    }
    class Employee extends Party{
        get id(){}
    }
    

12.9 折疊繼承體系

  • 目的:合并子類捏检,減少不必要的子類荞驴,降低系統(tǒng)復(fù)雜度

  • 場(chǎng)景:當(dāng)超類與子類沒(méi)有多大差別

  • 例子:

    class Employee{}
    class Sales extends Employee{}
    
    class Employee{}
    

12.10 以委托取代子類

  • 目的:增強(qiáng)類的擴(kuò)展性

  • 場(chǎng)景:當(dāng)子類可能存在多種類型上的變化

  • 例子

    class Booking{
        constructor(show, date){
            this._show = show;
            this._date = date;
        }
    }
    
    class PremiumBooking extends Booking{
        constructor(show, date, extras){
            super(show, date);
            this._extras = extras;
        }
    }
    
    class Booking{
        constructor(show, date){
            this._show = show;
            this._date = date;
            this._premium = null;
        }
        bePremium(extras){
            this._premium = new PremiumBookingDelegate(this, extras);
        }
    }
    
    class PremiumBookingDelegate{
        constructor(root, extras){
            this._root = root;
            this._extras = extras;
        }
    }
    

12.11 以委托取代超類

  • 目的:增強(qiáng)類的擴(kuò)展性

  • 場(chǎng)景:當(dāng)超類的部分方法不適用于子類,不清晰的繼承關(guān)系

  • 例子:

    class List{}
    class Stack extends List{}
    
    class List{}
    class Stack{
        constructor(){
            this._list = new List();
        }
    }
    

    超類的所有方法都適用于子類贯城,子類的所有實(shí)例都是超類的實(shí)例 => 使用繼承

    如果發(fā)現(xiàn)繼承有問(wèn)題(擴(kuò)展困難)熊楼,再使用以委托取代超類

轉(zhuǎn)自好友先偉大佬 https://github.com/xianweics/refator-code

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鲫骗,更是在濱河造成了極大的恐慌犬耻,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,386評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件执泰,死亡現(xiàn)場(chǎng)離奇詭異枕磁,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)术吝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門计济,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人排苍,你說(shuō)我怎么就攤上這事沦寂。” “怎么了淘衙?”我有些...
    開(kāi)封第一講書人閱讀 164,704評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵传藏,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我彤守,道長(zhǎng)毯侦,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書人閱讀 58,702評(píng)論 1 294
  • 正文 為了忘掉前任具垫,我火速辦了婚禮叫惊,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘做修。我一直安慰自己霍狰,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,716評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布饰及。 她就那樣靜靜地躺著蔗坯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪燎含。 梳的紋絲不亂的頭發(fā)上宾濒,一...
    開(kāi)封第一講書人閱讀 51,573評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音屏箍,去河邊找鬼绘梦。 笑死,一個(gè)胖子當(dāng)著我的面吹牛赴魁,可吹牛的內(nèi)容都是我干的卸奉。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼颖御,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼榄棵!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 39,230評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤疹鳄,失蹤者是張志新(化名)和其女友劉穎拧略,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體瘪弓,經(jīng)...
    沈念sama閱讀 45,680評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡垫蛆,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,873評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了腺怯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片月褥。...
    茶點(diǎn)故事閱讀 39,991評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖瓢喉,靈堂內(nèi)的尸體忽然破棺而出宁赤,到底是詐尸還是另有隱情,我是刑警寧澤栓票,帶...
    沈念sama閱讀 35,706評(píng)論 5 346
  • 正文 年R本政府宣布决左,位于F島的核電站,受9級(jí)特大地震影響走贪,放射性物質(zhì)發(fā)生泄漏佛猛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,329評(píng)論 3 330
  • 文/蒙蒙 一坠狡、第九天 我趴在偏房一處隱蔽的房頂上張望继找。 院中可真熱鬧,春花似錦逃沿、人聲如沸婴渡。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,910評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)边臼。三九已至,卻和暖如春假消,著一層夾襖步出監(jiān)牢的瞬間柠并,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 33,038評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工富拗, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留臼予,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,158評(píng)論 3 370
  • 正文 我出身青樓啃沪,卻偏偏與公主長(zhǎng)得像粘拾,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子谅阿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,941評(píng)論 2 355

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