【設(shè)計(jì)模式】設(shè)計(jì)原則之K.Y.D.L 四原則

K.Y.D.L 四原則

K:KISS(Keep it Simple and Stupid)簡(jiǎn)單原則
Y:YAGNI(You Ain't Gonna Need It)不編寫(xiě)不需要代碼原則
D:DRY(Don't repeat yourself)不要重復(fù)代碼原則
L:LOD(Law of Demter)迪米特原則(最少知識(shí)原則)

1. KISS(Keep it Simple and Stupid)原則

1.1 定義

盡量保持簡(jiǎn)單屋匕。

1.2 KISS 中簡(jiǎn)單的含義

1. 代碼行數(shù)越少越簡(jiǎn)單?

判斷 IP 地址是否合法的三種實(shí)現(xiàn)方式:

// 第一種實(shí)現(xiàn)方式: 使用正則表達(dá)式
public boolean isValidIpAddressV1(String ipAddress) {
  if (StringUtils.isBlank(ipAddress)) return false;
  String regex = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\."
          + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."
          + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."
          + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$";
  return ipAddress.matches(regex);
}

// 第二種實(shí)現(xiàn)方式: 使用現(xiàn)成的工具類
public boolean isValidIpAddressV2(String ipAddress) {
  if (StringUtils.isBlank(ipAddress)) return false;
  String[] ipUnits = StringUtils.split(ipAddress, '.');
  if (ipUnits.length != 4) {
    return false;
  }
  for (int i = 0; i < 4; ++i) {
    int ipUnitIntValue;
    try {
      ipUnitIntValue = Integer.parseInt(ipUnits[i]);
    } catch (NumberFormatException e) {
      return false;
    }
    if (ipUnitIntValue < 0 || ipUnitIntValue > 255) {
      return false;
    }
    if (i == 0 && ipUnitIntValue == 0) {
      return false;
    }
  }
  return true;
}

// 第三種實(shí)現(xiàn)方式: 不使用任何工具類
public boolean isValidIpAddressV3(String ipAddress) {
  char[] ipChars = ipAddress.toCharArray();
  int length = ipChars.length;
  int ipUnitIntValue = -1;
  boolean isFirstUnit = true;
  int unitsCount = 0;
  for (int i = 0; i < length; ++i) {
    char c = ipChars[i];
    if (c == '.') {
      if (ipUnitIntValue < 0 || ipUnitIntValue > 255) return false;
      if (isFirstUnit && ipUnitIntValue == 0) return false;
      if (isFirstUnit) isFirstUnit = false;
      ipUnitIntValue = -1;
      unitsCount++;
      continue;
    }
    if (c < '0' || c > '9') {
      return false;
    }
    if (ipUnitIntValue == -1) ipUnitIntValue = 0;
    ipUnitIntValue = ipUnitIntValue * 10 + (c - '0');
  }
  if (ipUnitIntValue < 0 || ipUnitIntValue > 255) return false;
  if (unitsCount != 3) return false;
  return true;
}

第一種使用正則表達(dá)式實(shí)現(xiàn)的方式逻住,代碼行數(shù)確定較少休蟹,但由于正則表達(dá)式本身較復(fù)雜骗村,難以理解浪默,所以疾就,整個(gè)代碼的實(shí)現(xiàn)并不簡(jiǎn)單肺然。這種實(shí)現(xiàn)方式導(dǎo)致代碼的可讀性和可維護(hù)性變差菠发,并不符合 KISS 原則王滤。

第二種和第三種實(shí)現(xiàn)思路是差不多的,唯一的區(qū)別是第二種實(shí)現(xiàn)方式使用了工具類滓鸠,而第三種完全是原生實(shí)現(xiàn)雁乡。第二種實(shí)現(xiàn)方式相比第三種,邏輯更加清晰糜俗,更容易讓人理解踱稍,所以,相比較而言悠抹,第二種更“簡(jiǎn)單”珠月,更加符合 KISS 原則。

2. 代碼邏輯復(fù)雜就違背 KISS 原則

// KMP algorithm: a, b分別是主串和模式串楔敌;n, m分別是主串和模式串的長(zhǎng)度啤挎。
public static int kmp(char[] a, int n, char[] b, int m) {
  int[] next = getNexts(b, m);
  int j = 0;
  for (int i = 0; i < n; ++i) {
    while (j > 0 && a[i] != b[j]) { // 一直找到a[i]和b[j]
      j = next[j - 1] + 1;
    }
    if (a[i] == b[j]) {
      ++j;
    }
    if (j == m) { // 找到匹配模式串的了
      return i - m + 1;
    }
  }
  return -1;
}

// b表示模式串,m表示模式串的長(zhǎng)度
private static int[] getNexts(char[] b, int m) {
  int[] next = new int[m];
  next[0] = -1;
  int k = -1;
  for (int i = 1; i < m; ++i) {
    while (k != -1 && b[k + 1] != b[i]) {
      k = next[k];
    }
    if (b[k + 1] == b[i]) {
      ++k;
    }
    next[i] = k;
  }
  return next;
}

KMP 是一個(gè)高效的匹配單模式字符串的算法卵凑,其實(shí)現(xiàn)本來(lái)就比較復(fù)雜庆聘,但效率卻非常高。如果對(duì)于處理長(zhǎng)文本字符串匹配這類復(fù)雜問(wèn)題氛谜,使用 KMP 算法也就是本身就復(fù)雜的問(wèn)題掏觉,用復(fù)雜的方法解決,并不違反 KISS 原則值漫。

如果在平時(shí)的開(kāi)發(fā)中,只是簡(jiǎn)單的字符串匹配织盼,這種情況下杨何,再使用 KMP 算法,那就算是違背 KISS 原則了沥邻。

從此可以看出危虱,是否違反某個(gè)設(shè)計(jì)原則,主要還是取決于當(dāng)前的應(yīng)用場(chǎng)景唐全。

1.3 如何寫(xiě)出滿足 KISS 原則的代碼

  1. 盡量不要使用同事不懂的技術(shù)來(lái)實(shí)現(xiàn)代碼埃跷,如:正則表達(dá)式...
  2. 不要重復(fù)造輪子蕊玷,要善于使用已有的工具類庫(kù)
  3. 避免過(guò)度優(yōu)化來(lái)犧牲代碼的可讀性

2. YAGNI(You Ain't Gonna Need It)

2.1 定義

不要去設(shè)計(jì)當(dāng)前用不到的功能;不要去編寫(xiě)當(dāng)前用來(lái)到的代碼弥雹。核心思想就是:不要過(guò)度設(shè)計(jì)垃帅。

2.2 例子

配置文件

目前系統(tǒng)暫時(shí)使用 Redis 來(lái)存儲(chǔ)配置信息,以后可能使用到 ZooKeeper剪勿。如果根據(jù) YAGNI 原則贸诚,在未用到 ZooKeeper 之前,沒(méi)有必要提前寫(xiě)好這部分代碼厕吉。當(dāng)然酱固,我們還是要預(yù)留好擴(kuò)展點(diǎn),等到需要的時(shí)候头朱,再去實(shí)現(xiàn) ZooKeeper 這部分的代碼运悲。

依賴開(kāi)發(fā)包

通常,項(xiàng)目中會(huì)依賴很多第三方的開(kāi)發(fā)包项钮,而有些開(kāi)發(fā)者嫌每次添加依賴配置較麻煩扇苞,往往會(huì)添加一個(gè)大而全的依賴配置,而將一些項(xiàng)目中根本用不到的第三方類庫(kù)也添加到項(xiàng)目中去寄纵。這樣做是違反 YAGNI 設(shè)計(jì)原則的鳖敷。

2.3 YAGNI 和 KISS 的區(qū)別

KISS 原則講的是“如何做”的問(wèn)題(盡可能保持簡(jiǎn)單)。

YAGNI 原則講的是“要不要做”的問(wèn)題(當(dāng)前不需要的就不要做)程拭。

3. DRY(Don't repeat youself)原則

3.1 定義

不要寫(xiě)重復(fù)的代碼定踱。

3.2 DRY 原則中關(guān)于重復(fù)的定義

1. 實(shí)現(xiàn)邏輯重復(fù)

public class UserAuthenticator {
  public void authenticate(String username, String password) {
    if (!isValidUsername(username)) {
      // ...throw InvalidUsernameException...
    }
    if (!isValidPassword(password)) {
      // ...throw InvalidPasswordException...
    }
    //...省略其他代碼...
  }

  private boolean isValidUsername(String username) {
    // check not null, not empty
    if (StringUtils.isBlank(username)) {
      return false;
    }
    // check length: 4~64
    int length = username.length();
    if (length < 4 || length > 64) {
      return false;
    }
    // contains only lowcase characters
    if (!StringUtils.isAllLowerCase(username)) {
      return false;
    }
    // contains only a~z,0~9,dot
    for (int i = 0; i < length; ++i) {
      char c = username.charAt(i);
      if (!(c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.') {
        return false;
      }
    }
    return true;
  }

  private boolean isValidPassword(String password) {
    // check not null, not empty
    if (StringUtils.isBlank(password)) {
      return false;
    }
    // check length: 4~64
    int length = password.length();
    if (length < 4 || length > 64) {
      return false;
    }
    // contains only lowcase characters
    if (!StringUtils.isAllLowerCase(password)) {
      return false;
    }
    // contains only a~z,0~9,dot
    for (int i = 0; i < length; ++i) {
      char c = password.charAt(i);
      if (!(c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') || c == '.') {
        return false;
      }
    }
    return true;
  }
}

上面的 isValidPassword()isValidUsername() 兩個(gè)函數(shù)的實(shí)現(xiàn)是一樣的,那這種算重復(fù)代碼么恃鞋?

實(shí)際上是不算的崖媚,雖然兩者的代碼實(shí)現(xiàn)是一樣的,但兩個(gè)函數(shù)干的其實(shí)是兩件事情恤浪,一個(gè)是效驗(yàn)用戶名畅哑,一個(gè)是效驗(yàn)密碼。盡管目前兩個(gè)函數(shù)的代碼是完全一樣的水由,這也只是說(shuō)剛好一樣而已荠呐。以后,隨著需求的變更砂客,兩個(gè)函數(shù)的實(shí)現(xiàn)邏輯就可能是不一樣的泥张。盡管代碼的實(shí)現(xiàn)邏輯是一樣的,但語(yǔ)義不同鞠值,所以媚创,其并不違反 DRY 原則。至于包含重復(fù)代碼的問(wèn)題彤恶,可以通過(guò)更小粒度的函數(shù)來(lái)達(dá)到代碼復(fù)用的目的钞钙。

2. 功能語(yǔ)義重復(fù)

public boolean isValidIp(String ipAddress) {
  if (StringUtils.isBlank(ipAddress)) return false;
  String regex = "^(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|[1-9])\\."
          + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."
          + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)\\."
          + "(1\\d{2}|2[0-4]\\d|25[0-5]|[1-9]\\d|\\d)$";
  return ipAddress.matches(regex);
}

public boolean checkIfIpValid(String ipAddress) {
  if (StringUtils.isBlank(ipAddress)) return false;
  String[] ipUnits = StringUtils.split(ipAddress, '.');
  if (ipUnits.length != 4) {
    return false;
  }
  for (int i = 0; i < 4; ++i) {
    int ipUnitIntValue;
    try {
      ipUnitIntValue = Integer.parseInt(ipUnits[i]);
    } catch (NumberFormatException e) {
      return false;
    }
    if (ipUnitIntValue < 0 || ipUnitIntValue > 255) {
      return false;
    }
    if (i == 0 && ipUnitIntValue == 0) {
      return false;
    }
  }
  return true;
}

上面的代碼鳄橘,雖然函數(shù)名和函數(shù)實(shí)現(xiàn)都是不一樣的,但功能是一樣的芒炼,都是用來(lái)判斷 IP 地址是否合法瘫怜。這種情況,往往是由于開(kāi)發(fā)同學(xué)分開(kāi)開(kāi)發(fā)導(dǎo)致的焕议。由于要實(shí)現(xiàn)的功能是完全一樣的宝磨,即使具體的函數(shù)實(shí)現(xiàn)不同,也是違反 DRY 原則的盅安,需要將刪除其中一個(gè)唤锉,讓整個(gè)項(xiàng)目統(tǒng)一使用一個(gè)實(shí)現(xiàn)。

功能語(yǔ)義重復(fù)可能導(dǎo)致的問(wèn)題:

項(xiàng)目中使用了兩個(gè)同樣功能的不同函數(shù)别瞭,如果哪天判斷的規(guī)則變了窿祥,只改了一個(gè),而另一個(gè)沒(méi)有被改變蝙寨,這種情況下晒衩,就可以會(huì)引入 BUG。

3. 代碼執(zhí)行重復(fù)

public class UserService {
  private UserRepo userRepo;//通過(guò)依賴注入或者IOC框架注入

  public User login(String email, String password) {
    boolean existed = userRepo.checkIfUserExisted(email, password);
    if (!existed) {
      // ... throw AuthenticationFailureException...
    }
    User user = userRepo.getUserByEmail(email);
    return user;
  }
}

public class UserRepo {
  public boolean checkIfUserExisted(String email, String password) {
    if (!EmailValidation.validate(email)) {
      // ... throw InvalidEmailException...
    }

    if (!PasswordValidation.validate(password)) {
      // ... throw InvalidPasswordException...
    }

    //...query db to check if email&password exists...
  }

  public User getUserByEmail(String email) {
    if (!EmailValidation.validate(email)) {
      // ... throw InvalidEmailException...
    }
    //...query db to get user by email...
  }
}

所謂代碼執(zhí)行重復(fù)墙歪,就是同一段代碼被執(zhí)行了多次听系。上面的代碼,在 login 函數(shù)中 email 的效驗(yàn)被調(diào)用了兩次虹菲,所以靠胜,是代碼執(zhí)行重復(fù),屬于違反了 DRY 原則毕源。

3.3 什么是代碼的復(fù)用性

代碼的復(fù)用性

指的是一段代碼可被復(fù)用的特性或能力浪漠。代碼的可復(fù)用性,是從代碼開(kāi)發(fā)者的角度來(lái)講的霎褐。

代碼復(fù)用

在開(kāi)發(fā)過(guò)程中址愿,盡量使用已經(jīng)存在的代碼。代碼復(fù)用冻璃,是從代碼使用者的角度來(lái)講的响谓。

DRY 原則

不要寫(xiě)重復(fù)的代碼。

如何提高代碼復(fù)用性

  1. 減少代碼耦合
  2. 滿足單一職責(zé)
  3. 模塊化
  4. 業(yè)務(wù)與非業(yè)務(wù)邏輯分離
  5. 通用代碼下沉
  6. 繼承俱饿、多態(tài)歌粥、抽象和封裝
  7. 應(yīng)用模塊方法等設(shè)計(jì)模式,復(fù)用通用的算骨架
  8. 運(yùn)用泛型技術(shù)編程拍埠,提高代碼的抽象程度

3.4 Rule of Three

也就是說(shuō),第一次編寫(xiě)代碼的時(shí)候土居,我們不考慮其復(fù)用性枣购;第二次遇到復(fù)用場(chǎng)景的時(shí)候嬉探,再進(jìn)行重構(gòu)使其復(fù)用。這里的 Three棉圈,指的是二涩堤,而不是三。

4. 迪米特原則(最少知識(shí)原則) LOD(Law of Demeter)

4.1 定義

不該有直接依賴關(guān)系的類之間分瘾,不要依賴胎围;有依賴關(guān)系的類之間,盡量只依賴必要的接口德召。

4.2 什么是高內(nèi)聚白魂、松耦合

高內(nèi)聚用來(lái)指導(dǎo)類本身的設(shè)計(jì),松耦合用來(lái)指導(dǎo)類與類之間依賴關(guān)系的設(shè)計(jì)上岗。高內(nèi)聚有助于松耦合福荸,松耦合又需要高內(nèi)聚的支持。

所謂高內(nèi)聚指的是:相近的功能應(yīng)該放到同一個(gè)類中肴掷,不相近的功能不要放到同一個(gè)類中敬锐。相近的功能往往會(huì)被同時(shí)修改,放到同一個(gè)類中呆瞻,修改比較集中台夺,代碼也容易維護(hù)。實(shí)際上痴脾,單一職責(zé)原則是實(shí)現(xiàn)代碼高內(nèi)聚的非常有效的設(shè)計(jì)原則颤介。

所謂松耦合指的是:類與類之間的依賴關(guān)系簡(jiǎn)單清晰。即有依賴關(guān)系的兩個(gè)類明郭,一個(gè)類的代碼改動(dòng)不會(huì)或很少導(dǎo)致依賴類代碼的改動(dòng)买窟。依賴注入、接口隔離原則薯定、依賴接口而非實(shí)現(xiàn)以及迪米特原則都是為了實(shí)現(xiàn)代碼的松耦合始绍。

4.3 不應(yīng)該有依賴關(guān)系的類之間,不要有依賴?yán)?/h3>
public class NetworkTransporter {
    // 省略屬性和其他方法...
    public Byte[] send(HtmlRequest htmlRequest) {
      //...
    }
}

public class HtmlDownloader {
  private NetworkTransporter transporter;//通過(guò)構(gòu)造函數(shù)或IOC注入
  
  public Html downloadHtml(String url) {
    Byte[] rawHtml = transporter.send(new HtmlRequest(url));
    return new Html(rawHtml);
  }
}

public class Document {
  private Html html;
  private String url;
  
  public Document(String url) {
    this.url = url;
    HtmlDownloader downloader = new HtmlDownloader();
    this.html = downloader.downloadHtml(url);
  }
  //...
}

存在問(wèn)題一:NetworkTransporter 類作用一個(gè)底層通信類话侄,其功能應(yīng)該盡可能通用亏推。而目前的設(shè)計(jì)依賴了太具體的 HtmlRequest 類,從這一種來(lái)講年堆,違反了迪米特原則吞杭,依賴了不該有直接依賴關(guān)系的類。

重構(gòu)后的 NetworkTransporter

public class NetworkTransporter {
    // 省略屬性和其他方法...
    public Byte[] send(String address, Byte[] data) {
      //...
    }
}

存在問(wèn)題二:Document 類存在三個(gè)主要問(wèn)題变丧。

  1. 構(gòu)造函數(shù)中邏輯過(guò)于復(fù)雜芽狗,耗時(shí)長(zhǎng),不應(yīng)該放在構(gòu)造函數(shù)中痒蓬,影響代碼的可測(cè)試性
  2. 所依賴的 HtmlDownloader 對(duì)象直接使用 new 的方式來(lái)創(chuàng)建童擎,違反了基于接口而非實(shí)現(xiàn)編程的設(shè)計(jì)思想滴劲,也會(huì)影響代碼的可測(cè)試性
  3. Document 網(wǎng)頁(yè)文檔沒(méi)必要依賴 HtmlDownloader 類,違反了迪米特原則

優(yōu)化后的 Document 類

public class Document {
  private Html html;
  private String url;
  
  public Document(String url, Html html) {
    this.html = html;
    this.url = url;
  }
  //...
}

// 通過(guò)一個(gè)工廠方法來(lái)創(chuàng)建Document
public class DocumentFactory {
  private HtmlDownloader downloader;
  
  public DocumentFactory(HtmlDownloader downloader) {
    this.downloader = downloader;
  }
  
  public Document createDocument(String url) {
    Html html = downloader.downloadHtml(url);
    return new Document(url, html);
  }
}

4.4 有依賴關(guān)系的類之間顾复,盡量只依賴必要的接口

public class Serialization {
  public String serialize(Object object) {
    String serializedResult = ...;
    //...
    return serializedResult;
  }
  
  public Object deserialize(String str) {
    Object deserializedResult = ...;
    //...
    return deserializedResult;
  }
}

上面代碼沒(méi)有什么問(wèn)題班挖,但如果把其放到具體的應(yīng)用場(chǎng)景中:假設(shè)在我們的項(xiàng)目中,有些類只用到了序列化方法芯砸,另一些類只用到了反序列化方法萧芙,那根據(jù)迪米特原則的后半部分“有依賴關(guān)系的兩個(gè)類,盡量依賴必要的接口”假丧,只用到了序列化的類不應(yīng)該依賴反序列化接口双揪,反之亦然。

滿足迪米特原則的優(yōu)化

public class Serializer {
  public String serialize(Object object) {
    String serializedResult = ...;
    ...
    return serializedResult;
  }
}

public class Deserializer {
  public Object deserialize(String str) {
    Object deserializedResult = ...;
    ...
    return deserializedResult;
  }
}

但滿足迪米特原則的優(yōu)化版本虎谢,又違反了高內(nèi)聚的設(shè)計(jì)思想盟榴,即相近的功能要放到同一個(gè)類中,方便統(tǒng)一修改婴噩。那如何優(yōu)化讓其即滿足高內(nèi)聚設(shè)計(jì)思想擎场,又滿足迪米特原則呢?

通過(guò)接口隔離原則几莽,引入兩個(gè)接口迅办,再根據(jù)多態(tài)特性,在使用序列化類的時(shí)候章蚣,依賴具體的單個(gè)u接口站欺,而非具體類。

引入接口隔離原則后的優(yōu)化版本

public interface Serializable {
  String serialize(Object object);
}

public interface Deserializable {
  Object deserialize(String text);
}

public class Serialization implements Serializable, Deserializable {
  @Override
  public String serialize(Object object) {
    String serializedResult = ...;
    ...
    return serializedResult;
  }
  
  @Override
  public Object deserialize(String str) {
    Object deserializedResult = ...;
    ...
    return deserializedResult;
  }
}

public class DemoClass_1 {
  private Serializable serializer;
  
  public Demo(Serializable serializer) {
    this.serializer = serializer;
  }
  //...
}

public class DemoClass_2 {
  private Deserializable deserializer;
  
  public Demo(Deserializable deserializer) {
    this.deserializer = deserializer;
  }
  //...
}

說(shuō)明

此文是根據(jù)王爭(zhēng)設(shè)計(jì)模式之美相關(guān)專欄內(nèi)容整理而來(lái),非原創(chuàng)。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末售葡,一起剝皮案震驚了整個(gè)濱河市锅劝,隨后出現(xiàn)的幾起案子拴泌,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蓬豁,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)菇肃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門(mén)地粪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人琐谤,你說(shuō)我怎么就攤上這事蟆技。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵付魔,是天一觀的道長(zhǎng)聊品。 經(jīng)常有香客問(wèn)我飞蹂,道長(zhǎng)几苍,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任陈哑,我火速辦了婚禮妻坝,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘惊窖。我一直安慰自己刽宪,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布界酒。 她就那樣靜靜地躺著圣拄,像睡著了一般。 火紅的嫁衣襯著肌膚如雪毁欣。 梳的紋絲不亂的頭發(fā)上庇谆,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音凭疮,去河邊找鬼饭耳。 笑死,一個(gè)胖子當(dāng)著我的面吹牛执解,可吹牛的內(nèi)容都是我干的寞肖。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼衰腌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼新蟆!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起右蕊,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤琼稻,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后尤泽,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體欣簇,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年坯约,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了熊咽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡闹丐,死狀恐怖横殴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤衫仑,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布梨与,位于F島的核電站,受9級(jí)特大地震影響文狱,放射性物質(zhì)發(fā)生泄漏粥鞋。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一瞄崇、第九天 我趴在偏房一處隱蔽的房頂上張望呻粹。 院中可真熱鬧,春花似錦苏研、人聲如沸等浊。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)筹燕。三九已至,卻和暖如春衅鹿,著一層夾襖步出監(jiān)牢的瞬間撒踪,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工塘安, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留糠涛,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓兼犯,卻偏偏與公主長(zhǎng)得像忍捡,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子切黔,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344