代碼重構(gòu)

為什么要重構(gòu)

  1. 重構(gòu)改進(jìn)軟件的設(shè)計

設(shè)計欠佳的程序往往需要更多的代碼吴超,重構(gòu)一個重要方向就是消除重復(fù)代碼

軟件變壞的途徑: 一個有架構(gòu)的軟件 > 修改代碼 > 沒有理解架構(gòu)設(shè)計 > 代碼沒有結(jié)構(gòu) > 修改代碼 > 難以讀懂原有設(shè)計 > 一個腐爛的架構(gòu)軟件

軟件變好的途徑: 一個腐爛的架構(gòu)軟件 > 修改代碼 > 改進(jìn)架構(gòu)設(shè)計 > 更具有結(jié)構(gòu) > 修改代碼 > 簡單易懂更易擴(kuò)展 > 一個好的架構(gòu)軟件

  1. 重構(gòu)使軟件更容易理解

編程的核心: 準(zhǔn)確說出我想要干什么汇恤,除了告訴計算機(jī)幢踏,還有其他的讀者

原來一個程序員要花一周時間來修改某段代碼搀矫,在重構(gòu)后更容易理解刑峡,現(xiàn)在只用花一小時就能搞定,這個就是時間成本阐污,人力成本休涤,軟件成本,公司成本的體現(xiàn)

  1. 重構(gòu)幫助找到bug

我不是一個特別好的程序員笛辟,我只是一個有著一些特別好的習(xí)慣的還不錯的程序員

特別好的程序員可以盯著一大段代碼可以找出bug, 我不行功氨,但是重構(gòu)了后序苏,代碼有了結(jié)構(gòu),脈絡(luò)疑故,bug會自動跑出來

  1. 重構(gòu)提高編程速度
    我花在重構(gòu)上的時間杠览,難道不是在降低開發(fā)速度嗎?

但是纵势,經(jīng)常會聽到這樣的故事: 一開始進(jìn)展的很快踱阿,但如今想要添加一個新功能需要的時間越來越長,需要花很多時間想著怎么把新功能塞進(jìn)現(xiàn)有的代碼庫(最好的當(dāng)然不是塞進(jìn)钦铁,是放進(jìn))软舌, 不斷的有bug, 修復(fù)起來也越來越慢,不斷的給補丁打補丁牛曹,逐漸變成了一個考古工作者


功能增加和需要時間的關(guān)系

何時重構(gòu)

三次法則: 第一次去做某件事盡管去做佛点,第二次做類似的事會有點反感,但是無論如何也要去做黎比,第三次再做類似的事超营,你就該重構(gòu)了。

  1. 預(yù)備性重構(gòu): 讓增加新功能更容易
    增加新功能時阅虫,對老代碼的微調(diào)演闭,會使工作容易很多

例子: 增加一個功能時,發(fā)現(xiàn)有一個函數(shù)跟我功能很類似颓帝,但是里面幾個字段或者值不一樣米碰,如果不重構(gòu),你就會把代碼復(fù)制過來购城,修改幾個值吕座,這就導(dǎo)致重復(fù)代碼,將來修改代碼就要改兩次瘪板,如果重構(gòu)下老的函數(shù)吴趴,增加一個參數(shù),這樣就是預(yù)備性重構(gòu)

  1. 幫助理解的重構(gòu): 使代碼更易讀懂

要把腦子里的理解轉(zhuǎn)移到代碼本身侮攀,這份知識才保存的更久锣枝,同事也能看到

給一兩個變量改名,讓他們更清晰的表達(dá)意圖
一個長函數(shù)拆開幾個小函數(shù)魏身,更易理解
已經(jīng)理解了代碼意圖惊橱,但是邏輯過于迂回復(fù)雜,精簡下更好

  1. 有計劃的重構(gòu)和見機(jī)行事的重構(gòu)
    上面兩個都是見機(jī)行事的重構(gòu)箭昵,但是當(dāng)功能增加到一定的時候税朴,簡單的重構(gòu)會有瓶頸,會發(fā)現(xiàn)一開始考慮不周的架構(gòu)設(shè)計,那么現(xiàn)在就需要有計劃的重構(gòu)

  2. 長期重構(gòu)
    但是很多重構(gòu)會花費幾個星期正林,幾個月的時間泡一,還有一大堆混亂的依賴關(guān)系,很多人參與觅廓,不可能停下來完全重構(gòu)鼻忠,那么可以每個人都達(dá)成共識,每天往想改進(jìn)的方向推動一點點杈绸,但是保持基本的功能不變帖蔓,比如要換掉一個庫,可以引入新的抽象瞳脓,兼容兩個庫的接口塑娇,等調(diào)用方慢慢切換過來,這樣換掉原來的庫就簡單多了

  3. 代碼復(fù)審的時候重構(gòu)(code review)
    很多時候自己看不出劫侧,或者經(jīng)驗不足埋酬,重構(gòu)后仍然不夠好,那么就需要有專門的code review, 來幫助我們更好的重構(gòu)代碼

何時不該重構(gòu)

  1. 看見一堆凌亂的代碼烧栋,但是我不需要修改的時候写妥,如果丑陋的代碼被隱藏在一個API下,就可以容忍它的丑陋审姓,等理解工作原理后珍特,再重構(gòu)
  2. 重寫比重構(gòu)還容易的,就別重構(gòu)了

怎么重構(gòu)

1. 命名規(guī)范

好的命名是整潔代碼的核心邑跪,使用范圍越廣的越要注意命名

來看一句神秘的代碼次坡,用一個變量表示高度呼猪,單位m

var height_rice = 4; // 高度為4米的變量, rice寫成米的英文

改變函數(shù)聲明

好辦法: 先寫一句注釋描述這個函數(shù)的作用画畅,再把這句注釋變成函數(shù)名字

function calc(height, width) {
    return height * width;
}
function calcArea(height, width) {
    return height * width;
}

變量改名

var a = height * width;
var area = height * width;

2. 重復(fù)代碼

如果在一個地方以上看到相同的代碼結(jié)構(gòu),就要設(shè)法將他們合二為一, 這個時候需要提煉函數(shù)來提供統(tǒng)一的使用方式:

提煉函數(shù)

什么時候把代碼放進(jìn)獨立的函數(shù): 將意圖與實現(xiàn)分開

function printOwing(invoice) {
  printBanner();
  let outstanding  = calculateOutstanding();

  //print details
  console.log(`name: ${invoice.customer}`);
  console.log(`amount: ${outstanding}`);  
}

可以看到上面是想要打印日志的意圖宋距,至于怎么打印則是實現(xiàn)轴踱,所以提取函數(shù)如下,至于命名谚赎,則是秉承次函數(shù)是 "做什么" 來命名:

function printOwing(invoice) {
  printBanner();
  let outstanding  = calculateOutstanding();
  printDetails(outstanding);

  function printDetails(outstanding) {
    console.log(`name: ${invoice.customer}`);
    console.log(`amount: ${outstanding}`);
  }
}

如果代碼是相似而不是完全相同淫僻,那么使用移動語句來讓相關(guān)的代碼,結(jié)構(gòu)在一起壶唤,這是提煉函數(shù)的前提雳灵,別看這個很簡單, 很多的重構(gòu)都是從這里開始

移動語句
下面是一段計算商品訂單經(jīng)費的代碼闸盔,完全沒有分類悯辙,很難理解業(yè)務(wù)流程

const pricingPlan = retrievePricingPlan();
const order = retreiveOrder();
const baseCharge = pricingPlan.base;
let charge;
const chargePerUnit = pricingPlan.unit;
const units = order.units;
let discount;
charge = baseCharge + units * chargePerUnit;
let discountableUnits = Math.max(units - pricingPlan.discountThreshold, 0);
discount = discountableUnits * pricingPlan.discountFactor;
if (order.isRepeat) discount += 20;
charge = charge - discount;
chargeOrder(charge);

采用移動語句之后,把相同的功能移動到一起分類,流程清晰躲撰,之后才能提取函數(shù)來進(jìn)一步重構(gòu)代碼

// 報價計劃
const pricingPlan = retrievePricingPlan();
const baseCharge = pricingPlan.base;
const chargePerUnit = pricingPlan.unit;

// 訂單數(shù)量
const order = retreiveOrder();
const units = order.units;

// 折扣
let discount;
let discountableUnits = Math.max(units - pricingPlan.discountThreshold, 0);
discount = discountableUnits * pricingPlan.discountFactor;

// 具體經(jīng)費
let charge;
if (order.isRepeat) discount += 20;
charge = baseCharge + units * chargePerUnit;
charge = charge - discount;
chargeOrder(charge);

函數(shù)上移
如果重復(fù)代碼位于繼承的子類中的時候针贬,可以把相同的代碼提到父類,避免子類之間互相調(diào)用

class Employee {...}

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

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

可以看到上訴子類都有相同的name()方法拢蛋,可以把方法上移到父類中

class Employee {
  get name() {...}
}

class Salesman extends Employee {...}
class Engineer extends Employee {...}

3. 過長的函數(shù)

老程序員的經(jīng)驗: 活的最長桦他,最好的程序,其中的函數(shù)都比較短谆棱,函數(shù)越長快压,越難理解,小函數(shù)易于理解的關(guān)鍵還是在于良好的命名垃瞧,好的命名就能讓人了解函數(shù)的作用嗓节,可以參考我的一個原則: 每當(dāng)感覺需要以注釋來說明點什么的時候,我們就需要把說明的東西寫進(jìn)一個獨立的函數(shù)里,并以其用途(而非實現(xiàn)手法)命名皆警, 一定要注意函數(shù) "做什么" 和 “怎么做”之間的語義理解拦宣,掌握了這點,就掌握了函數(shù)用法的精髓信姓。

在把長函數(shù)分解成小函數(shù)過程中鸵隧,常常會遇到函數(shù)內(nèi)有大量的參數(shù)臨時變量,如果你只是提取函數(shù)意推,就會把許多參數(shù)傳遞給被提煉的函數(shù)豆瘫,從可讀性上面來說沒有任何提升

以查詢?nèi)〈R時變量

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

上面生成了臨時變量basePrice, 完全可以放到類屬性里面,這樣在提取函數(shù)的時候菊值,就少了一個臨時變量外驱,不用當(dāng)成參數(shù)傳遞了

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

引入?yún)?shù)對象
對于過長的參數(shù)列表,引入?yún)?shù)對象是個好辦法腻窒,這樣可以簡化為一個參數(shù)結(jié)構(gòu)

function amountInvoiced(startDate, endDate) {...}
function amountReceived(startDate, endDate) {...}
function amountOverdue(startDate, endDate) {...}

上面代碼每個函數(shù)都在傳遞三個時間參數(shù)昵宇,就可以提煉一個時間的數(shù)據(jù)類來統(tǒng)一管理

class DateRange  {
  string startDate;
  string middleDate
  string endDate;
}
function amountInvoiced(dateRange) {...}
function amountReceived(dateRange) {...}
function amountOverdue(dateRange) {...}

劃重點:這項重構(gòu)方法具有更深層的改變 *新的數(shù)據(jù)結(jié)構(gòu) -> 重組函數(shù)來使用新結(jié)構(gòu) -> 捕捉圍繞新數(shù)據(jù)結(jié)構(gòu)的公用函數(shù) -> 構(gòu)建新的類來組合新的數(shù)據(jù)結(jié)構(gòu)和函數(shù) -> 形成新的抽象概念 -> 改變整個軟件架構(gòu)圖景, 所以說儿子,新結(jié)構(gòu)的一小步才會有軟件架構(gòu)的一大步

函數(shù)組合成類

當(dāng)分成獨立的函數(shù)之后瓦哎,這不是代碼的終點,如果發(fā)現(xiàn)一組函數(shù)形影不離的操作著同一塊數(shù)據(jù)(做為參數(shù)傳給函數(shù))柔逼,此時就是時候組建一個類了

例如上面引入?yún)?shù)對象后的函數(shù)和數(shù)據(jù)結(jié)構(gòu)組合如下

class Amount {  // 金額類
    DateRange  dateRange; // 時間范圍字段
    Invoiced() {...};  // 發(fā)票金額方法
    received() {...}; // 收支金額方法
    overdue() {...}; // 欠款金額方法
}

使用類的好處:當(dāng)修改上面Amount類的dateRange這類核心數(shù)據(jù)時蒋譬,依賴于此的數(shù)據(jù),比如發(fā)票愉适,收支犯助,欠款等會與核心數(shù)據(jù)保持一致

4. 簡化條件邏輯

分解條件表達(dá)式

復(fù)雜的條件邏輯是最常導(dǎo)致復(fù)雜度上升的地方之一,所以適當(dāng)?shù)姆纸馑麄兛梢愿宄谋砻髅總€分支的作用

if (!aDate.isBefore(plan.summerStart) && !aDate.isAfter(plan.summerEnd))
  charge = quantity * plan.summerRate;
else
  charge = quantity * plan.regularRate + plan.regularServiceCharge;

上面代碼很難直觀看出此條件是什么作用维咸,把這些條件和實現(xiàn)提取為函數(shù)后就非常清晰了剂买,夏天時候的支出和其他季節(jié)的支出不同

if (summer())
  charge = summerCharge();
else
  charge = regularCharge();

合并條件表達(dá)式
有時候發(fā)現(xiàn)一串條件檢查:檢查條件各不相同扑媚,最終行為卻一致,這種情況可以使用‘邏輯或‘ 或‘邏輯與’合并為一個條件表達(dá)式

if (anEmployee.seniority < 2) return 0;
if (anEmployee.monthsDisabled > 12) return 0;
if (anEmployee.isPartTime) return 0;

上面都是返回0的情況雷恃,就可以提煉為一個函數(shù)統(tǒng)一返回

if (isNotEligibleForDisability()) return 0;

function isNotEligibleForDisability() {
  return ((anEmployee.seniority < 2)
          || (anEmployee.monthsDisabled > 12)
          || (anEmployee.isPartTime));
}

簡化嵌套條件表達(dá)式

條件表達(dá)式通常有兩種風(fēng)格疆股,第一種:兩個條件分支都屬于正常行為,這個時候可以用 if...else...的條件表達(dá)式倒槐;第二種: 只有一個條件分支是正常行為旬痹,另一個則是異常行為,發(fā)生情況很罕見讨越,此時應(yīng)該單獨檢查該條件两残,改條件為真時立即返回

function getPayAmount() {
  let result;
  if (isDead)
    result = deadAmount();
  else {
    if (isSeparated)
      result = separatedAmount();
    else {
      if (isRetired)
        result = retiredAmount();
      else
        result = normalPayAmount();
    }
  }
  return result;
}

上面代碼是一段根據(jù)不同員工狀態(tài)發(fā)工資的邏輯,死了有撫恤金把跨,辭退的有補償金人弓,退休了有退休金,平常就正常發(fā)工資着逐,很顯然崔赌,正常發(fā)工資是大概率事件,其他的都可以簡化為獨立判斷語句然后返回耸别,這樣代碼清晰

function getPayAmount() {
  if (isDead) return deadAmount();
  if (isSeparated) return separatedAmount();
  if (isRetired) return retiredAmount();
  return normalPayAmount();
}

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

復(fù)雜的條件邏輯是編程中最難理解的東西健芭,多態(tài)是面向?qū)ο缶幊痰年P(guān)鍵特征之一,大部分簡單的條件判斷用if...else..或者switch...case...無關(guān)緊要秀姐,但是如果有四五個或更多的復(fù)雜條件邏輯慈迈,多態(tài)是改善這種情況的有力工具

function plumage(bird) {
    switch (bird.type) {
        case 'EuropeanSwallow':
            return "average";
        case 'AfricanSwallow':
            return (bird.numberOfCoconuts > 2) ? "tired" : "average";
        case 'NorwegianBlueParrot':
            return (bird.voltage > 100) ? "scorched" : "beautiful";
        default:
            return "unknown";
}

把具體的實現(xiàn)封裝到類里方法,你可能會問省有,這不是還有switch和case嗎痒留?注意上面只是一個獲取羽毛的方法里用了swtich和case,如果以后我們又要根據(jù)鳥的種類獲取鳥的大小蠢沿,壽命等情況呢伸头,又要在很多方法里用這些討厭的swtich..case, 但是把他們用多態(tài)抽象為類后,可以像下面使用類似構(gòu)造工廠的方式來創(chuàng)建不同品種的鳥搏予,他們的接口都相同熊锭,后面只管調(diào)用了弧轧,往深處說雪侥,可以繼續(xù)用抽象工廠,或者控制反轉(zhuǎn)(IOC)等特性(VanGo平臺底層實現(xiàn)的精髓 ' . ' )徹底干掉這些swtich...case來實現(xiàn)動態(tài)創(chuàng)建對象精绎,當(dāng)然這些深入的東西這里就不討論了速缨。

 function createBird(bird) {
    switch (bird.type) {
    case 'EuropeanSwallow':
      return new EuropeanSwallow(bird);
    case 'AfricanSwallow':
      return new AfricanSwallow(bird);
    case 'NorweigianBlueParrot':
      return new NorwegianBlueParrot(bird);
    default:
      return new Bird(bird);
    }
  }

class EuropeanSwallow {
  get plumage() {
    return "average";
  }
class AfricanSwallow {
  get plumage() {
     return (this.numberOfCoconuts > 2) ? "tired" : "average";
  }
class NorwegianBlueParrot {
  get plumage() {
     return (this.voltage > 100) ? "scorched" : "beautiful";
  }

5. 可變數(shù)據(jù)

對數(shù)據(jù)的經(jīng)常修改是導(dǎo)致出乎意料的結(jié)果和難以發(fā)現(xiàn)的bug, 我在一處更新了數(shù)據(jù),沒有意識到另一處用期望著完全不同的數(shù)據(jù)代乃,我們要約束數(shù)據(jù)更新

封裝變量
一個好的習(xí)慣: 對于所有可變數(shù)據(jù)旬牲,只要它的作用域超出了單個函數(shù)仿粹,我就會將其封裝起來,只允許通過函數(shù)訪問原茅,數(shù)據(jù)的作用域越大吭历,封裝就越重要

let defaultOwner = {firstName: "Martin", lastName: "Fowler"};

每次獲取或者設(shè)置值的時候通過函數(shù),可以監(jiān)控或者統(tǒng)一修改內(nèi)部來改變真正的值擂橘,避免了很多bug

let defaultOwnerData = {firstName: "Martin", lastName: "Fowler"};
export function defaultOwner()       {return defaultOwnerData;}
export function setDefaultOwner(arg) {defaultOwnerData = arg;}

拆分變量
變量有各種不同的用途晌区,要避免臨時變量被多次賦值,如果變量承擔(dān)多個責(zé)任通贞,就應(yīng)該被分解為多個有獨立意義的變量

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

將查詢函數(shù)和修改函數(shù)分離
如果函數(shù)只提供一個值朗若,沒有任何看得到的副作用,證明是個好函數(shù)昌罩,一個好的規(guī)則是: 任何有返回值的函數(shù)哭懈,都不應(yīng)該有看的見的副作用,如果遇到一個 “既有返回值又有副作用” 的函數(shù)茎用,證明這里會有“看不見的”可變數(shù)據(jù)遣总,就要試著將他們分離

function getTotalOutstandingAndSendBill() {
  const result = customer.invoices.reduce((total, each) => each.amount + total, 0);
  sendBill();
  return result;
}

可以看到在上面get函數(shù)里,sendBill()和這個函數(shù)沒有任何關(guān)系轨功,這就是副作用彤避,此時就要將它分離出來,這是要保證函數(shù)的純凈夯辖,所謂的“純函數(shù)”琉预,只有職責(zé)分離,才能干大事

function totalOutstanding() {
  return customer.invoices.reduce((total, each) => each.amount + total, 0);  
}
function sendBill() {
  emailGateway.send(formatBill(customer));
}

6. 繼承關(guān)系

子類父類功能隔離
比如一些子類公用函數(shù)蒿褂,字段就要函數(shù)上移或者字段上移到父類來統(tǒng)一管理圆米,相反如果是子類特有的函數(shù),字段啄栓,就要用函數(shù)下移或者字段下移到子類分別實現(xiàn)娄帖,這里就不寫具體例子了,希望讀者可以自行領(lǐng)會

提煉超類
一般的面向?qū)ο蟮乃枷胧牵?code>繼承必須是真實的分類對象模型的繼承昙楚,比如鴨子繼承動物近速;但是更實用的方法是: 發(fā)現(xiàn)一些共同的元素,就把他們抽取到一起堪旧,于是有了繼承關(guān)系.

class Department {
  get totalAnnualCost() {...}
  get name() {...}
  get headCount() {...}
}

class Employee {
  get annualCost() {...}
  get name() {...}
  get id() {...}
}

上面部門和職員都有名字和年成本這兩個屬性削葱,那么我們把他們提到一個超類中,名叫組織淳梦,也有名字析砸,和年成本,這樣子類部門和職員可以通過覆蓋實現(xiàn)自己的年成本計算爆袍,同時他們公司名字可能相同的首繁,就復(fù)用父類代碼

class Party {
  get name() {...}
  get annualCost() {...}
}

class Department extends Party {
  get annualCost() {...}
  get headCount() {...}
}

class Employee extends Party {
  get annualCost() {...}
  get id() {...}
}

以委托取代子類
繼承是根據(jù)分類用于把屬于某一類公共的數(shù)據(jù)和行為放到超類中作郭,每個子類根據(jù)需求覆寫部分屬性,這是繼承的本質(zhì)弦疮,但是由于這種本質(zhì)體系夹攒,體現(xiàn)了他的缺點:繼承只能處理一個分類方向上面的變化,但是子類上導(dǎo)致行為不同的原因有很多種, 比如人我根據(jù)'年齡'來繼承分類胁塞,分為‘年輕人’和'老人'芹助,但是對于'富人'和'窮人'這個分類來看,其實相同年齡的'年輕人'行為是很不同的闲先,你們說是吧

class Order {
  get daysToShip() {
    return this._warehouse.daysToShip;
  }
}

class PriorityOrder extends Order {
  get daysToShip() {
    return this._priorityPlan.daysToShip;
  }
}

把繼承的寫法状土,提到超類的委托里面,這樣就是組合伺糠,所謂“對象組合優(yōu)于類繼承”也是這個道理蒙谓,一個原則是,先用繼承解決代碼復(fù)用問題训桶,發(fā)現(xiàn)分類不對了累驮,再改為委托

class Order {
  get daysToShip() {
    return (this._priorityDelegate)
      ? this._priorityDelegate.daysToShip
      : this._warehouse.daysToShip;
  }
}

class PriorityOrderDelegate {
  get daysToShip() {
    return this._priorityPlan.daysToShip
  }
}

以委托取代超類
如果超類的一些函數(shù)對于子類并不適合,就說明我們不應(yīng)該通過繼承來獲得超類的功能舵揭,而改為委托谤专,合理的繼承關(guān)系有一個重要特征: 子類的所有實例都應(yīng)該是超類的實例,通過超類的接口來使用子類的實例應(yīng)該完全不出問題

class List {...}
class Stack extends List {...}

比如我們實現(xiàn)的棧類(Stack)原本繼承了列表類(List)午绳,但是發(fā)現(xiàn)很多列表的方法不適合棧置侍,那就改用委托(組合)關(guān)系來把列表當(dāng)成一個屬性放在子類中,然后封裝需要用到列表類的方法即可

class Stack {
  constructor() {
    this._storage = new List();
  }
}
class List {...}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拦焚,一起剝皮案震驚了整個濱河市蜡坊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌赎败,老刑警劉巖秕衙,帶你破解...
    沈念sama閱讀 206,968評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異僵刮,居然都是意外死亡据忘,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評論 2 382
  • 文/潘曉璐 我一進(jìn)店門搞糕,熙熙樓的掌柜王于貴愁眉苦臉地迎上來勇吊,“玉大人,你說我怎么就攤上這事寞宫∠舾#” “怎么了?”我有些...
    開封第一講書人閱讀 153,220評論 0 344
  • 文/不壞的土叔 我叫張陵辈赋,是天一觀的道長鲫忍。 經(jīng)常有香客問我,道長钥屈,這世上最難降的妖魔是什么悟民? 我笑而不...
    開封第一講書人閱讀 55,416評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮篷就,結(jié)果婚禮上射亏,老公的妹妹穿的比我還像新娘。我一直安慰自己竭业,他們只是感情好智润,可當(dāng)我...
    茶點故事閱讀 64,425評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著未辆,像睡著了一般窟绷。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上咐柜,一...
    開封第一講書人閱讀 49,144評論 1 285
  • 那天兼蜈,我揣著相機(jī)與錄音,去河邊找鬼拙友。 笑死为狸,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的遗契。 我是一名探鬼主播辐棒,決...
    沈念sama閱讀 38,432評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼牍蜂!你這毒婦竟也來了涉瘾?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,088評論 0 261
  • 序言:老撾萬榮一對情侶失蹤捷兰,失蹤者是張志新(化名)和其女友劉穎立叛,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贡茅,經(jīng)...
    沈念sama閱讀 43,586評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡秘蛇,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,028評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了顶考。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片赁还。...
    茶點故事閱讀 38,137評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖驹沿,靈堂內(nèi)的尸體忽然破棺而出艘策,到底是詐尸還是另有隱情,我是刑警寧澤渊季,帶...
    沈念sama閱讀 33,783評論 4 324
  • 正文 年R本政府宣布朋蔫,位于F島的核電站罚渐,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏驯妄。R本人自食惡果不足惜荷并,卻給世界環(huán)境...
    茶點故事閱讀 39,343評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望青扔。 院中可真熱鬧源织,春花似錦、人聲如沸微猖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽凛剥。三九已至侠仇,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間当悔,已是汗流浹背傅瞻。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留盲憎,地道東北人嗅骄。 一個月前我還...
    沈念sama閱讀 45,595評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像饼疙,于是被迫代替她去往敵國和親溺森。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,901評論 2 345

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

  • 閱讀《重構(gòu)》的筆記獻(xiàn)上。 重構(gòu)的定義 重構(gòu)是在不改變軟件可觀察行為的前提下改善其內(nèi)部結(jié)構(gòu)磅甩。 重構(gòu)的節(jié)奏 以微小的步...
    陳宇明閱讀 11,616評論 13 64
  • 代碼重構(gòu)簡介:(英語:Code refactoring)重構(gòu)就是在不改變軟件系統(tǒng)外部行為的前提下卷要,改善它的內(nèi)部結(jié)構(gòu)...
    TaiXiang閱讀 2,936評論 0 2
  • 一渣聚、為什么要代碼重構(gòu)(Refactoring) 在不改變系統(tǒng)功能的情況下,改變系統(tǒng)的實現(xiàn)方式僧叉。為什么要這么做奕枝?投入...
    iYeso閱讀 388評論 0 3
  • 8月19日隘道,校長以新人IG.WXZ的身份擔(dān)任IG的AD首發(fā)對陣VG。 IG這場比賽看下來,或許算不上精彩谭梗,比賽全程...
    漁樵煮江談閱讀 388評論 0 0
  • 大學(xué)畢業(yè)后忘晤,她進(jìn)了一所國際公司的設(shè)計部,她的生活平平淡淡默辨,早出晚歸德频,有次工作時苍息,她想到了他缩幸。 “他可能已經(jīng)和那個女...
    喵小絨閱讀 123評論 0 0