2022-05-03 代碼重構(gòu)案例

如何code review代碼累盗?

代碼code review 從大處著眼可以從可讀性寒矿、可維護(hù)性、可擴(kuò)展若债、可復(fù)用符相、可測(cè)試等方面來衡量;落實(shí)到具體細(xì)節(jié)拆座,可以從非功能性功能性兩個(gè)方面來進(jìn)行衡量主巍。

非功能性

  1. 目錄設(shè)置是否合理、模塊劃分是否清晰挪凑、代碼結(jié)構(gòu)是否滿足“高內(nèi)聚孕索,低耦合”特性;
  2. 是否遵循經(jīng)典設(shè)計(jì)原則與設(shè)計(jì)思想躏碳,如:SOLID搞旭、DRY、KISS、YAGNI和LOD等肄渗;
  3. 設(shè)計(jì)模式是否應(yīng)用合理镇眷,是否過度設(shè)計(jì);
  4. 代碼是否易擴(kuò)展翎嫡,是否滿足開閉原則欠动;
  5. 代碼是否可復(fù)用,是否可以復(fù)用已有代碼或類庫惑申,是否存在重復(fù)造輪子具伍;
  6. 代碼是否易測(cè)試,UT對(duì)正常和異常邊界情況是否覆蓋全面圈驼;
  7. 代碼是否可讀人芽,是否滿足編碼規(guī)范,如:命名是否準(zhǔn)確達(dá)意绩脆、注釋是否恰當(dāng)萤厅、代碼風(fēng)格是否一致、編程是否存在多層嵌套靴迫,復(fù)雜邏輯或多個(gè)入?yún)⑶闆r是否進(jìn)行拆分或封裝等惕味。

功能性

  1. 代碼設(shè)計(jì)是否符合功能預(yù)期;
  2. 邏輯是否正確矢劲,異常邊界情況是否處置合理赦拘;
  3. 日志打印是否得當(dāng),是否方便排查問題芬沉?
  4. 接口是否易用躺同,是否支持CUD場(chǎng)景事務(wù)和冪等操作;
  5. 并發(fā)場(chǎng)景下代碼是否線程安全丸逸,是否存在共享變量蹋艺;
  6. 性能是否有優(yōu)化空間,如:SQL黄刚、算法是否可以繼續(xù)優(yōu)化捎谨;
  7. 是否存在安全漏洞,輸入憔维、輸出校驗(yàn)是否全面合理涛救;

案例

需求

為了方便排查問題,請(qǐng)?jiān)O(shè)計(jì)一個(gè)ID 生成器业扒,每次請(qǐng)求會(huì)將生成的ID 保存在 Servlet 線程的 ThreadLocal 中检吆,每次打印日志的時(shí)候,我們從請(qǐng)求上下文中取出請(qǐng)求 ID程储,跟日志一塊輸出蹭沛。這樣臂寝,同一個(gè)請(qǐng)求的所有日志都包含同樣的請(qǐng)求 ID 信息,我們就可以通過請(qǐng)求 ID 來搜索同一個(gè)請(qǐng)求的所有日志了摊灭。

實(shí)現(xiàn)

@Slf4j
public class IdGenerator {
    /**
     * 獲取請(qǐng)求ID
     *
     * @return
     */
    public static String generate(){
        String id = "";
        try{
            String hostName = InetAddress.getLocalHost().getHostName();
            String[] tokens = hostName.split("\\.");
            if(tokens.length > 0){
                hostName = tokens[tokens.length-1];
            }
            char[] randomChars = new char[8];
            int count = 0;
            Random random = new Random();
            while(count < 8){
                int randomAscii = random.nextInt(122);

                if(randomAscii >= 48 && randomAscii <= 57){
                    randomChars[count] = (char) (('0')+(randomAscii -48));
                    count++;
                }else if(randomAscii >= 65 && randomAscii <= 90) {
                    randomChars[count] = (char) (('A') + (randomAscii - 65));
                    count++;
                }else if(randomAscii >= 97 && randomAscii <= 122){
                    randomChars[count] = (char) (('a')+(randomAscii -97));
                    count++;
                }
            }
            id = String.format("%s-%d-%s",hostName,System.currentTimeMillis(),new String(randomChars));
        }catch (UnknownHostException e){
            log.warn("Failed to get the host name.",e);
        }
        return id;
    }
}

優(yōu)化

非功能分析

待優(yōu)化名稱 是否需要優(yōu)化 備注
非功能 1 IdGenerator只有一個(gè)類咆贬,不涉及目錄設(shè)置、模塊劃分和代碼結(jié)構(gòu)
非功能 2 IdGenerator只有一個(gè)類帚呼,不涉及設(shè)計(jì)原則和設(shè)計(jì)思想
非功能 3 IdGenerator只有一個(gè)類掏缎,沒有使用設(shè)計(jì)模式,不存在過度設(shè)計(jì)萝挤。
非功能 4 IdGenerator設(shè)計(jì)成實(shí)現(xiàn)類而非接口形式御毅,調(diào)用者直接依賴實(shí)現(xiàn)而非接口,違反基于接口而非實(shí)現(xiàn)編程的設(shè)計(jì)思想怜珍,不過目前場(chǎng)景下也滿足需求,如果或者有其他常見的ID生成則需要進(jìn)行改動(dòng)凤粗。
非功能 5
非功能 6 IdGenerator.generate()為靜態(tài)函數(shù)不利于測(cè)試酥泛,同時(shí)依賴運(yùn)行環(huán)境(本機(jī)名)、時(shí)間函數(shù)嫌拣、隨機(jī)函數(shù)也不利于測(cè)試
非功能 7 代碼的可讀性不好柔袁,一方面沒有注釋,另一方面存在大量魔法值异逐,隨機(jī)串部分生成邏輯比較復(fù)雜不宜理解捶索。

功能分析

待優(yōu)化名稱 是否需要優(yōu)化 備注
功能 1 雖然生成ID并非絕對(duì)唯一,但是對(duì)于追蹤打印日志來說灰瞻,是可以接受小概率 ID 沖突的腥例,滿足我們預(yù)期的業(yè)務(wù)需求
功能 2 獲取hostName部分,沒有處理hostName == null的情況酝润,同時(shí)針對(duì)異常的處理也有問題燎竖。null和異常處理可以參考異常處理部分。
功能 3 日志的作用是方便debug排查問題要销,此處日志打印是合理构回。
功能 4 IdGenerator 只暴露一個(gè) generate() 接口供調(diào)用者使用,接口的定義簡單明了疏咐,不存在不易用問題
功能 5 generate()函數(shù)不存在共享變量纤掸,線程安全,并發(fā)場(chǎng)景下不存在線程安全問題浑塞。
功能 6 生成ID需要獲取本機(jī)名借跪,獲取本機(jī)名比較耗時(shí);同時(shí)生成隨機(jī)字符串極端情況下需要循環(huán)多次才能生成符合要求的字符串(09缩举,az垦梆,A~Z)匹颤,也需要優(yōu)化下。
功能 7

異常處理

函數(shù)出錯(cuò)返回?cái)?shù)據(jù)類型托猩,我總結(jié)了 4 種情況分別是:錯(cuò)誤碼印蓖、NULL 值、空對(duì)象京腥、異常對(duì)象赦肃。

錯(cuò)誤碼

在沒有異常語法機(jī)制的語言中,常用錯(cuò)誤碼來處理錯(cuò)誤公浪,比如:code >= 0 表示接口調(diào)用成功他宛;code < 0表示接口調(diào)用失敗欠气;同時(shí)code值也可以賦予特殊意義厅各。

NULL值

在多數(shù)編程語言中,我們用 NULL 來表示“不存在”這種語義预柒。對(duì)于查找函數(shù)來說队塘,數(shù)據(jù)不存在并非一種異常情況,是一種正常行為宜鸯,所以返回表示不存在語義的 NULL 值比返回異常更加合理憔古。

空對(duì)象

返回 NULL 值有各種弊端,對(duì)此有一個(gè)比較經(jīng)典的應(yīng)對(duì)策略淋袖,那就是應(yīng)用空對(duì)象設(shè)計(jì)模式鸿市。當(dāng)函數(shù)返回的數(shù)據(jù)是字符串類型或者集合類型的時(shí)候,我們可以用空字符串或空集合替代 NULL 值即碗,來表示不存在的情況焰情。這樣,我們?cè)谑褂煤瘮?shù)的時(shí)候拜姿,就可以不用做 NULL 值判斷烙样。

異常對(duì)象

對(duì)于接口拋出的異常,我們有三種處理方法:直接吞掉蕊肥、直接往上拋出谒获、包裹成新的異常拋出。

適用場(chǎng)景
  • 直接吞掉:如果 被調(diào)用方拋出的異常是可以恢復(fù)壁却,的調(diào)用方并不關(guān)心此異常批狱,我們完全可以在 調(diào)用方中拋出的異常吞掉;
  • 直接往上拋出:如果被調(diào)用方拋出的異常對(duì)調(diào)用方來說展东,也是可以理解的赔硫、關(guān)心的 ,并且在業(yè)務(wù)概念上有一定的相關(guān)性盐肃,我們可以選擇直接將 被調(diào)用方拋出的異常 re-throw爪膊;
  • 包裹成新的異常拋出:如果被調(diào)用方拋出的異常太底層权悟,對(duì)調(diào)用方來說,缺乏背景去理解推盛、且業(yè)務(wù)概念上無關(guān)峦阁,我們可以將它重新包裝成調(diào)用方可以理解的新異常,然后 re-throw耘成。

優(yōu)化計(jì)劃

重構(gòu)代碼的過程需要遵循循序漸進(jìn)榔昔,小步快跑思路。每次改動(dòng)一點(diǎn)點(diǎn)瘪菌,測(cè)試通過之后再進(jìn)行下一部分撒会。針對(duì)本次重構(gòu)計(jì)劃可以分成四部分進(jìn)行,具體如下:

  • 第一輪:提高可讀性师妙;

    具體來說分別是:

    1. hostName 變量不應(yīng)該被重復(fù)使用诵肛,尤其當(dāng)這兩次使用時(shí)的含義還不同的時(shí)候;
    2. 將獲取 hostName 的代碼抽離出來默穴,定義為 getLastfieldOfHostName() 函數(shù)曾掂;
    3. 刪除代碼中的魔法數(shù);
    4. generate() 函數(shù)中的三個(gè) if 邏輯重復(fù)了壁顶,且實(shí)現(xiàn)過于復(fù)雜,我們要對(duì)其進(jìn)行簡化溜歪;
    5. 對(duì) IdGenerator 類重命名若专,并且抽象出對(duì)應(yīng)的接口。
  • 第二輪:提高可測(cè)試性蝴猪;

    具體來說分別是:

    1. generate() 函數(shù)定義為靜態(tài)函數(shù)调衰,會(huì)影響使用該函數(shù)的代碼的可測(cè)試性,需要改為非靜態(tài)函數(shù)自阱;
    2. generate() 函數(shù)的代碼實(shí)現(xiàn)依賴運(yùn)行環(huán)境(本機(jī)名)嚎莉、時(shí)間函數(shù)、隨機(jī)函數(shù)測(cè)試性不好需要進(jìn)行對(duì)相應(yīng)邏輯進(jìn)行拆分和封裝沛豌,具體來下:1趋箩、從 getLastfieldOfHostName() 函數(shù)中,將邏輯比較復(fù)雜的那部分代碼剝離出來加派,定義為 getLastSubstrSplittedByDot() 函數(shù)叫确;2、將 generateRandomAlphameric() 和 getLastSubstrSplittedByDot() 這兩個(gè)函數(shù)的訪問權(quán)限設(shè)置為 protected芍锦。這樣做的目的是竹勉,可以直接在單元測(cè)試中通過對(duì)象來調(diào)用兩個(gè)函數(shù)進(jìn)行測(cè)試。3娄琉、給 generateRandomAlphameric() 和 getLastSubstrSplittedByDot() 兩個(gè)函數(shù)添加@VisibleForTesting次乓。告訴其他人說吓歇,這兩個(gè)函數(shù)本該是 private 訪問權(quán)限的,之所以提升訪問權(quán)限到 protected票腰,只是為了測(cè)試城看,只能用于單元測(cè)試中。
    3. 針對(duì)異常和特殊值進(jìn)行適當(dāng)處理丧慈,拋出調(diào)用方理解的異常析命。
  • 第三輪:編寫完成UT,寫單元測(cè)試的時(shí)候逃默,測(cè)試對(duì)象是函數(shù)定義的功能鹃愤,而非具體的實(shí)現(xiàn)邏輯;

    針對(duì)generate()函數(shù)可以有三個(gè)不同功能的定義完域;

    • 如果我們把 generate() 函數(shù)的功能定義為:“生成一個(gè)隨機(jī)唯一 ID”软吐,那我們只要測(cè)試多次調(diào)用 generate() 函數(shù)生成的 ID 是否唯一即可。
    • 如果我們把 generate() 函數(shù)的功能定義為:“生成一個(gè)只包含數(shù)字吟税、大小寫字母和中劃線的唯一 ID”凹耙,那我們不僅要測(cè)試 ID 的唯一性,還要測(cè)試生成的 ID 是否只包含數(shù)字肠仪、大小寫字母和中劃線肖抱。
    • 如果我們把 generate() 函數(shù)的功能定義為:“生成唯一 ID,格式為:{主機(jī)名 substr}-{時(shí)間戳}-{8 位隨機(jī)數(shù)}异旧。在主機(jī)名獲取失敗時(shí)意述,返回:null-{時(shí)間戳}-{8 位隨機(jī)數(shù)}”,那我們不僅要測(cè)試 ID 的唯一性吮蛹,還要測(cè)試生成的 ID 是否完全符合格式要求荤崇。

    總結(jié):UT如何寫,關(guān)鍵看你如何定義函數(shù)潮针。

  • 第四輪:添加完整注釋术荤,注釋主要就是寫清楚:做什么、為什么每篷、怎么做瓣戚、怎么用,對(duì)一些邊界條件雳攘、特殊情況進(jìn)行說明带兜,以及對(duì)函數(shù)輸入、輸出吨灭、異常進(jìn)行說明刚照。;

優(yōu)化結(jié)果

/**
 * IdGenerator接口 重構(gòu)一 提高代碼可擴(kuò)展性
 */
public interface IdGenerator {
    public String generate() throws IdGenerationFailureException;
}
/**
 * LogTraceIdGenerator接口 重構(gòu)一 提高代碼可擴(kuò)展性
 */
public interface LogTraceIdGenerator extends IdGenerator{
}
/**
 * 用于生成隨機(jī) ID 的 Id 生成器喧兄。//重構(gòu)四:添加注釋
 *
 *
 * 此類生成的 ID 不是絕對(duì)唯一的无畔,
 * 但重復(fù)的可能性非常低啊楚。
 */
@Slf4j
public class RandomIdGenerator implements LogTraceIdGenerator{//重構(gòu)一 提高代碼可擴(kuò)展性
    /**
     * 生成隨機(jī) ID。只有在極端情況下浑彰,才能生成重復(fù)ID恭理。
     *
     * @return隨機(jī) ID
     */
    @Override
    public String generate() throws IdGenerationFailureException {//重構(gòu)一
        String substrOfHostName = null;
        try {
            substrOfHostName = getLastfieldOfHostName();
        } catch (UnknownHostException e) { //重構(gòu)二:封裝新異常繼續(xù)拋出
           throw new IdGenerationFailureException("host name is empty.");
        }
        //if(substrOfHostName == null || substrOfHostName.isEmpty()){//重構(gòu)二:異常處理
        //    throw new IdGenerationFailureException("host name is empty.");
        //}
        long currentTimeMillis = System.currentTimeMillis();

        String randomString = generateRandomAlphameric(8);//重構(gòu)二 提供代碼可測(cè)試性
        String id = String.format("%s-%d-%s", substrOfHostName, currentTimeMillis, randomString);
        return id;
    }

    /**
     * 獲取本地主機(jī)名和
     * 提取由分隔符 '.' 拆分的名稱字符串的最后一個(gè)字段。
     *
     * @return 主機(jī)名的最后一個(gè)字段郭变。如果未獲取主機(jī)名颜价,則返回 null。
     */
    private String getLastfieldOfHostName() throws UnknownHostException {// 重構(gòu)一
        String substrOfHostName = null;
        //try {
        String hostName = InetAddress.getLocalHost().getHostName();

        //重構(gòu)二 NULL判斷
        if (hostName == null || hostName.isEmpty()) {
            throw new UnknownHostException();
        }
        //重構(gòu)二 邏輯拆分
        substrOfHostName = getLastSubstrSplittedByDot(hostName);
        //String[] tokens = hostName.split("\\.");
        //substrOfHostName = tokens[tokens.length -1];

        //}catch (Exception e){////重構(gòu)二:異常處理诉濒,直接拋出
        //    log.warn("Failed to get the host name.",e);
        //}
        return substrOfHostName;

    }

    /**
     * 獲取 {@hostName} 的最后一個(gè)字段周伦,該字段由 delemiter '.' 拆分。
     *
     * @param hostName 主機(jī)名不應(yīng)為空
     * @return {@hostName} 的最后一個(gè)字段未荒。如果 {@hostName} 為空字符串专挪,則返回空字符串。
     */
    @VisibleForTesting //重構(gòu)二
    protected String getLastSubstrSplittedByDot(String hostName) {//重構(gòu)二 提供代碼可測(cè)試性
        //重構(gòu)二:NULL 判斷片排,如果傳入NULL 則拋運(yùn)行異常
        if(hostName == null || hostName.isEmpty()){
            throw new IllegalArgumentException("host name is empty");
        }
        String[] tokens = hostName.split("\\.");
        String substrOfHostName = tokens[tokens.length -1];
        return substrOfHostName;
    }

    /**
     * 生成隨機(jī)字符串寨腔,其中
     * 僅包含數(shù)字、大寫字母和小寫字母率寡。
     *
     * @param length 長度應(yīng)不小于0
     * @return 隨機(jī)字符串 如果 {@length} 為 0 則返回空字符串
     *
     */
    @VisibleForTesting //重構(gòu)二
    protected String generateRandomAlphameric(int length) {//重構(gòu)一 復(fù)雜邏輯拆分 //重構(gòu)二 為了提高測(cè)試性迫卢,將private 改為 protected
        if(length < 0){ //重構(gòu)二 邊界處理
            throw new IllegalArgumentException("parameter length is illegal.");
        }

        char[] randomChars = new char[length];
        int count = 0;
        Random random = new Random();
        while (count < length){
            int maxAscii = 'z';
            int radomAscii = random.nextInt(maxAscii);
            //重構(gòu)一 提高代碼可讀性
            boolean isDigit = radomAscii >= '0' && radomAscii < '9';
            boolean isLowercase = radomAscii >= 'a' && radomAscii < 'z';
            boolean isUppercase = radomAscii >= 'A' && radomAscii < 'Z';
            if(isDigit || isLowercase || isUppercase){
                randomChars[count] = (char) radomAscii;
                count++;
            }
        }
        return new String(randomChars);
    }
}

/**
 * RandomIdGeneratorTest類 重構(gòu)三:編寫UT
 */
public class RandomIdGeneratorTest {

    @Test
    public void testGetLastSubstrSplittedByDot(){
        RandomIdGenerator generator = new RandomIdGenerator();
        String actualSubstr = generator.getLastSubstrSplittedByDot("field1.field2.field3");
        Assert.assertEquals("field3",actualSubstr);

        actualSubstr = generator.getLastSubstrSplittedByDot("field1");
        Assert.assertEquals("field1",actualSubstr);

        actualSubstr = generator.getLastSubstrSplittedByDot("field1#field2#field3");
        Assert.assertEquals("field1#field2#field3",actualSubstr);
    }

    @Test
    public void testGetLastSubstrSplittedByDot_nullOrEmpty(){
        RandomIdGenerator generator = new RandomIdGenerator();
        String actualSubstr = generator.getLastSubstrSplittedByDot("");
        Assert.assertEquals("",actualSubstr);

        actualSubstr = generator.getLastSubstrSplittedByDot(null);
        Assert.assertNull(actualSubstr);
    }

    @Test
    public void testGenerateRandomAlphameric(){
        RandomIdGenerator generator = new RandomIdGenerator();
        String randomString = generator.generateRandomAlphameric(8);
        Assert.assertNotNull(randomString);
        Assert.assertEquals(8,randomString.length());
        for (char s : randomString.toCharArray()) {
            Assert.assertTrue((s >= '0' && s < '9') || (s >= 'a' && s < 'z') || (s >= 'A' && s < 'Z'));
        }
    }

    @Test
    public void testGenerateRandomAlphameric_lengthEqualsOrLessThanZero(){
        RandomIdGenerator generator = new RandomIdGenerator();
        String randomString = generator.generateRandomAlphameric(0);
        Assert.assertEquals("",randomString);

        randomString = generator.generateRandomAlphameric(-1);
        Assert.assertNull(randomString);
    }

    /**
     * generate() 函數(shù)的功能定義為:“生成唯一 ID,格式為:{主機(jī)名 substr}-{時(shí)間戳}-{8 位隨機(jī)數(shù)}
     */
    @Test
    public void testGenerate(){
        RandomIdGenerator generator = new RandomIdGenerator();
        String id1 = generator.generate();
        Assert.assertNotNull(id1);
        Assert.assertTrue(id1.length() > 0);
        for (char c: id1.toCharArray()) {
            Assert.assertTrue((c == '-')||(c >= '0' && c < '9') || (c >= 'a' && c < 'z') || (c >= 'A' && c < 'Z'));

        }
        String id2 = generator.generate();
        Assert.assertNotNull(id1);
        Assert.assertTrue(id1.length() > 0);
        for (char c: id2.toCharArray()) {
            Assert.assertTrue((c == '-')||(c >= '0' && c < '9') || (c >= 'a' && c < 'z') || (c >= 'A' && c < 'Z'));

        }

        Assert.assertNotEquals(id1,id2);
    }

    /**
     * generate() 函數(shù)在主機(jī)名獲取失敗時(shí)冶共,返回:null-{時(shí)間戳}-{8 位隨機(jī)數(shù)}”,測(cè)試生成的 ID 是否完全符合格式要求靖避。
     */
    @Test
    public void testGenerate_withoutSubHostName(){
        RandomIdGenerator generator = new RandomIdGenerator();
        generator.getLastSubstrSplittedByDot(null);

        String id = generator.generate();
        Assert.assertNotNull(id);
        Assert.assertTrue(id.length() > 0);
        String[] split = id.split("-");
        Assert.assertTrue(split[0] == null);
        Assert.assertTrue(System.currentTimeMillis() == Long.valueOf(split[1]));
        for (char c: split[2].toCharArray()) {
            Assert.assertTrue((c >= '0' && c < '9') || (c >= 'a' && c < 'z') || (c >= 'A' && c < 'Z'));

        }
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
禁止轉(zhuǎn)載,如需轉(zhuǎn)載請(qǐng)通過簡信或評(píng)論聯(lián)系作者比默。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市盆犁,隨后出現(xiàn)的幾起案子命咐,更是在濱河造成了極大的恐慌,老刑警劉巖谐岁,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件醋奠,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡伊佃,警方通過查閱死者的電腦和手機(jī)窜司,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來航揉,“玉大人塞祈,你說我怎么就攤上這事∷浚” “怎么了议薪?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵尤蛮,是天一觀的道長。 經(jīng)常有香客問我斯议,道長产捞,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任哼御,我火速辦了婚禮坯临,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘恋昼。我一直安慰自己看靠,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布焰雕。 她就那樣靜靜地躺著衷笋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪矩屁。 梳的紋絲不亂的頭發(fā)上辟宗,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音吝秕,去河邊找鬼泊脐。 笑死,一個(gè)胖子當(dāng)著我的面吹牛烁峭,可吹牛的內(nèi)容都是我干的容客。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼约郁,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼缩挑!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起鬓梅,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤供置,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后绽快,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芥丧,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年坊罢,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了续担。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡活孩,死狀恐怖物遇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤挎挖,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布这敬,位于F島的核電站,受9級(jí)特大地震影響蕉朵,放射性物質(zhì)發(fā)生泄漏崔涂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一始衅、第九天 我趴在偏房一處隱蔽的房頂上張望冷蚂。 院中可真熱鬧,春花似錦汛闸、人聲如沸蝙茶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽隆夯。三九已至,卻和暖如春别伏,著一層夾襖步出監(jiān)牢的瞬間蹄衷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工厘肮, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留愧口,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓类茂,卻偏偏與公主長得像耍属,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子巩检,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

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