《重構(gòu)》- 處理概括關(guān)系

一. Full Up Field(字段上移)

介紹

  1. 場(chǎng)景
    兩個(gè)子類擁有相同的字段诬垂。
  2. 手法
    將該字段移至超類回季。

動(dòng)機(jī)

本項(xiàng)重構(gòu)去除了重復(fù)的數(shù)據(jù)聲明互纯,并且將使用該字段的行為從子類移至超類壮吩,去除重復(fù)的行為檐蚜。

二. Pull Up Method(函數(shù)上移)

介紹

  1. 場(chǎng)景
    有些函數(shù)蟆技,在各個(gè)子類中產(chǎn)生完全相同的結(jié)果玩敏。
  2. 手法
    將該函數(shù)移至超類。

動(dòng)機(jī)

  1. 避免行為重復(fù)是很重要的质礼。重復(fù)自身只會(huì)成為錯(cuò)誤的滋生地聊品,此外別無價(jià)值。
  2. 無論何時(shí)几苍,只要系統(tǒng)之內(nèi)出現(xiàn)重復(fù)翻屈,你就面臨“修改其中一個(gè)卻未能修改另一個(gè)”的風(fēng)險(xiǎn)。

三. Pull Up Constructor Body(構(gòu)造函數(shù)本體上移)

介紹

  1. 場(chǎng)景
    你在各個(gè)子類中擁有一些構(gòu)造函數(shù)妻坝,它們的本體幾乎完全一致伸眶。
  2. 手法
    在超類中新建一個(gè)構(gòu)造函數(shù),并在子類構(gòu)造函數(shù)中調(diào)用它刽宪。

動(dòng)機(jī)

如果你看見各個(gè)子類中的函數(shù)有共同行為厘贼,第一個(gè)念頭應(yīng)該是將共同行為提煉到一個(gè)獨(dú)立函數(shù)中,然后將這個(gè)函數(shù)提升到超類圣拄。

范例

重構(gòu)前

class Employee {
  _name;
  _id;
  constructor(name, id) {
    this._name = name
    this._id = id
  }

  isPriviliged() {}

  assignCar() {}
}

class Manager extends Employee {
  constructor(name, id, grade) {
    this._name = name
    this._id = id
    this._grade = grade
    if(this.isPriviliged()) {
      this.assignCar()
    }
  }
}

重構(gòu)后

class Employee {
  _name;
  _id;
  constructor(name, id) {
    this._name = name
    this._id = id
  }

  initialize() {
    if(this.isPriviliged()) {
      this.assignCar()
    }
  }

  isPriviliged() {}

  assignCar() {}
}

class Manager extends Employee {
  constructor(name, id, grade) {
    super(name, id)
    this._grade = grade
    this.initialize()
  }
}

四. Push Down Method(函數(shù)下移)

介紹

  1. 場(chǎng)景
    超類中的某個(gè)函數(shù)只與部分(而非全部)子類有關(guān)嘴秸。
  2. 手法
    將這個(gè)函數(shù)移到相關(guān)的那些子類去。

動(dòng)機(jī)

Push Down Method(函數(shù)下移)與Pull Up Method(函數(shù)上移)恰恰相反庇谆。

五. Push Down Field(字段下移)

介紹

  1. 場(chǎng)景
    超類中的某個(gè)字段只被部分(而非全部)子類用到岳掐。
  2. 手法
    將這個(gè)字段移到需要它的那些子類去。

動(dòng)機(jī)

Push Down Field(字段下移)與Full Up Field(字段上移)恰恰相反饭耳。

六. Extract Subclass(提煉子類)

介紹

  1. 場(chǎng)景
    類中的某些特性只被某些(而非全部)實(shí)例用到串述。
  2. 手法
    新建一個(gè)子類,將上面所說的那一部分特性移到子類中寞肖。

范例

重構(gòu)前

class JobItem{
  constructor(unitPrice, quantity, isLabor, employee) {
    this._unitPrice = unitPrice
    this._quantity = quantity
    this._isLabor = isLabor
    this._employee = employee
  }

  getUnitPrice() {
    return this._isLabor ? this._employee.getRate() : this._unitPrice
  }

  getTotalPrice() {
    return this.getUnitPrice() * this._quantity
  }

  getQuantity() {
    return this._quantity
  }

  getEmployee() {
    return this._employee
  }
}

class Employee {
  constructor(rate) {
    this._rate = rate
  }

  getRate() {
    return this._rate
  }
}

重構(gòu)后

class JobItem{
  constructor(unitPrice, quantity) {
    this._unitPrice = unitPrice
    this._quantity = quantity
  }

  getUnitPrice() {
    return this._unitPrice
  }

  getTotalPrice() {
    return this.getUnitPrice() * this._quantity
  }

  getQuantity() {
    return this._quantity
  }
}

class LaborItem extends JobItem{
  constructor(quantity, employee) {
    super(0, quantity)
    this._employee = employee
  }

  getUnitPrice() {
    return this._employee.getRate() 
  }

  getEmployee() {
    return this._employee
  }
}

class Employee {
  constructor(rate) {
    this._rate = rate
  }

  getRate() {
    return this._rate
  }
}

七. Extract Superclass(提煉超類)

介紹

  1. 場(chǎng)景
    兩個(gè)類有相似特性纲酗。
  2. 手法
    為這兩個(gè)類建立一個(gè)超類,將相同特性移至超類新蟆。

動(dòng)機(jī)

  1. 重復(fù)代碼是系統(tǒng)中最糟糕的東西之一觅赊。如果你在不同地方做同一件事情,一旦需要修改那些動(dòng)作琼稻,你就得平白做更多的修改吮螺。

范例

重構(gòu)前

class Employee{
  constructor(name, id, annualCost) {
    this._name = name
    this._id = id
    this._annualCost = annualCost
  }

  getAnnualCost() {
    return this._annualCost
  }

  getId() {
    return this._id
  }

  getName() {
    return this._name
  }
}

class Department{
  _staff = []

  constructor(name) {
    this._name = name
  }

  getStaff() {
    return this._staff
  }

  getTotalAnnualCost() {
    let result = 0
    this.getStaff().forEach(item => {
      result += item.getAnnualCost()
    })
    return result
  }

  getHeadCount() {
    return this._staff.length
  }

  addStaff(arg) {
    return this._staff.push(arg)
  }

  getName() {
    return this._name
  }
}

重構(gòu)后

class Party{
  constructor(name) {
    this._name = name
  }

  getName() {
    return this._name
  }

  getAnnualCost() {}
}


class Employee extends Party{
  constructor(name, id, annualCost) {
    super(name)
    this._id = id
    this._annualCost = annualCost
  }

  getAnnualCost() {
    return this._annualCost
  }

  getId() {
    return this._id
  }
}

class Department extends Party{

  _staff = []

  constructor(name) {
    super(name)
  }

  getStaff() {
    return this._staff
  }

  getAnnualCost() {
    let result = 0
    this.getStaff().forEach(item => {
      result += item.getAnnualCost()
    })
    return result
  }

  getHeadCount() {
    return this._staff.length
  }

  addStaff(arg) {
    return this._staff.push(arg)
  }
}

八. Extract Interface(提煉接口)

介紹

  1. 場(chǎng)景
    若干客戶使用類接口中的同一子集,或者兩個(gè)類的接口有部分相同。
  2. 手法
    將相同的子集提煉到一個(gè)獨(dú)立接口中规脸。

范例

JavaScript沒有接口的概念坯约,無法用代碼演示該重構(gòu)手法熊咽。

九. Collapse Hierarchy(折疊繼承體系)

介紹

  1. 場(chǎng)景
    超類和子類之間無太大區(qū)別莫鸭。
  2. 手法
    將它們合為一體。

動(dòng)機(jī)

  1. 繼承體系很容易變得過分復(fù)雜横殴。重構(gòu)繼承體系往往是將函數(shù)和字段在體系中上下移動(dòng)被因。
  2. 如果發(fā)現(xiàn)某個(gè)子類并未帶來該有的價(jià)值,需要把超類與子類合并起來衫仑。

十. Form Template Method(塑造模板函數(shù))

介紹

  1. 場(chǎng)景
    你有一些子類梨与,其中相應(yīng)的某些函數(shù)以相同順序執(zhí)行類似的操作,但各個(gè)操作的細(xì)節(jié)上有所不同文狱。
  2. 手法
    將這些操作分別放進(jìn)獨(dú)立函數(shù)中粥鞋,并保持他們都有相同的簽名,于是原函數(shù)也就變得相同了瞄崇。然后將原函數(shù)上移至超類呻粹。

動(dòng)機(jī)

  1. 繼承是避免重復(fù)行為的一個(gè)強(qiáng)大工具。只要你看見兩個(gè)子類之中有類似的函數(shù)苏研,就可以把他們提升到超類等浊。
  2. 如果子類中兩個(gè)函數(shù)以相同順序執(zhí)行大致相近的操作,但是各操作不完全相同摹蘑。這種情況下我們可以將執(zhí)行操作的序列移至超類筹燕,并借助多態(tài)保證各操作仍得以保持差異性。這樣的函數(shù)被稱為模板方法衅鹿。

范例

重構(gòu)前

class Customer {
  statement() {
    const rentals = this._rentals
    // \n表示換行
    let result = `Rental Record for ${this.getName()}\n`
    rentals.forEach(item => {
      //\t表示制表符
      result += `\t${item.getMovie().getTitle()}\t${item.getCharge()}\n`
    })

    result += `Amount owed is ${this.getTotalCharge()} \n`
    result += `You earned ${this.getTotalFrequentRenterPoints()} frequent renter points`
    return result
  }

  htmlStatement() {
    const rentals = this._rentals
    let result = `<h1>Rentals for<EM>${this.getName()}</EM></h1><P>\n`
    rentals.forEach(item => {
      result += `${item.getMovie().getTitle()}: ${item.getCharge()}<BR>\n`
    })
    result += `<P> You owe <EM> ${this.getTotalCharge()}</EM><P>\n`
    result += `On this rental you earned <EM> ${this.getTotalFrequentRenterPoints()} </EM> frequent renter points <P>`
    return result
  }
}

重構(gòu)后

class Customer {
  statement() {
    return new TextStatement().value(this)
  }

  htmlStatement() {
    return new HtmlStatement().value(this)
  }
}

class Statement {
  value(customer) {
    const rentals = customer._rentals
    let result = this.headerString(customer)
    rentals.forEach(item => {
      result += this.eachRentalString(item)
    })
    result += this.footerString(customer)
    return result
  }

  headerString(customer) {}

  eachRentalString(customer) {}

  footerString(customer) {}
}

class TextStatement extends Statement {
  headerString(customer) {
    return `Rental Record for ${customer.getName()}\n`
  }

  eachRentalString(rental) {
    return `\t${rental.getMovie().getTitle()}\t${rental.getCharge()}\n`
  }

  footerString(customer) {
    return `Amount owed is ${customer.getTotalCharge()} \n You earned ${customer.getTotalFrequentRenterPoints()} frequent renter points`
  }
}

class HtmlStatement extends Statement {
  headerString(customer) {
    return `<h1>Rentals for<EM>${customer.getName()}</EM></h1><P>\n`
  }

  eachRentalString(rental) {
    return `${rental.getMovie().getTitle()}: ${rental.getCharge()}<BR>\n`
  }

  footerString(customer) {
    return `<P> You owe <EM> ${customer.getTotalCharge()}</EM><P>\n On this rental you earned <EM> ${customer.getTotalFrequentRenterPoints()} </EM> frequent renter points <P>`
  }
}

十一. Replace Inheritance with Delegation(以委托取代繼承)

介紹

  1. 場(chǎng)景
    某個(gè)子類只使用超類接口中的一部分撒踪,或是根本不需要繼承而來的數(shù)據(jù)。
  2. 手法
    在子類中新建一個(gè)字段用以保存超類大渤;調(diào)整子類函數(shù)糠涛,令它改而委托超類;然后去掉兩者之間的繼承關(guān)系兼犯。

動(dòng)機(jī)

  1. 繼承是個(gè)好東西忍捡,但有時(shí)候它并不是你要的。
  2. 如果從超類中繼承了許多用不到的方法或數(shù)據(jù)切黔,就應(yīng)該以委托取代繼承砸脊。

范例

重構(gòu)前

class MyStack extends Vector {
  push(element) {
    this.insertElement(element, 0)
  }

  pop() {
    const result = this.firstElement()
    this.removeElementAt(0)
    return result
  }
}

重構(gòu)后

class MyStack {
  _vector = new Vector()

  push(element) {
    this._vector.insertElement(element, 0)
  }

  pop() {
    const result = this._vector.firstElement()
    this._vector.removeElementAt(0)
    return result
  }
  size() {
    return this.Vector.size()
  }

  isEmpty() {
    return this._vector.isEmpty()
  }
}

十二. Replace Delegation with Inheritance(以繼承取代委托)

介紹

  1. 場(chǎng)景
    你在兩個(gè)類之間使用委托關(guān)系,并經(jīng)常為整個(gè)接口編寫許多極簡(jiǎn)單的委托函數(shù)纬霞。
  2. 手法
    讓委托類繼承受委托類凌埂。

動(dòng)機(jī)

  1. 本重構(gòu)與Replace Inheritance with Delegation(以委托取代繼承)恰恰相反。
  2. 如果你發(fā)現(xiàn)自己需要使用受委托類中的所有函數(shù)诗芜,并且費(fèi)了很大力氣編寫所有極簡(jiǎn)的委托函數(shù)瞳抓,本重構(gòu)可以幫助你輕松回頭使用繼承埃疫。
  3. 如果你并沒有使用受委托類的所有函數(shù),那么就不應(yīng)該將委托關(guān)系替換為繼承關(guān)系孩哑。
  4. 如果受托對(duì)象被不止一個(gè)其他對(duì)象共享栓霜,并且受托對(duì)象時(shí)可變的,你就不能將委托關(guān)系替換為繼承關(guān)系横蜒。

范例

重構(gòu)前

class Employee {
  _person = new Person()

  getName() {
    return this._person.getName()
  }

  setName(arg) {
    this._person.setName(arg)
  }

  toString() {
    return `Emp: ${this._person.getLastName()}`
  }
}


class Person{
  _name 

  getName() {
    return this._name
  }

  setName(arg) {
    this._name = arg
  } 

  getLastName() {
    return this._name.substring(this._name.lastIndexOf(' ') + 1)
  }
}

重構(gòu)后

class Employee extends Person {
  toString() {
    return `Emp: ${this.getLastName()}`
  }
}


class Person{
  _name 

  getName() {
    return this._name
  }

  setName(arg) {
    this._name = arg
  } 

  getLastName() {
    return this._name.substring(this._name.lastIndexOf(' ') + 1)
  }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末胳蛮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子丛晌,更是在濱河造成了極大的恐慌仅炊,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件澎蛛,死亡現(xiàn)場(chǎng)離奇詭異抚垄,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)谋逻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門呆馁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人斤贰,你說我怎么就攤上這事智哀。” “怎么了荧恍?”我有些...
    開封第一講書人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵瓷叫,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我送巡,道長(zhǎng)摹菠,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任骗爆,我火速辦了婚禮次氨,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘摘投。我一直安慰自己煮寡,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開白布犀呼。 她就那樣靜靜地躺著幸撕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪外臂。 梳的紋絲不亂的頭發(fā)上坐儿,一...
    開封第一講書人閱讀 51,165評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音貌矿,去河邊找鬼炭菌。 笑死逛漫,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的尽楔。 我是一名探鬼主播投储,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼第练,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了娇掏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤婴梧,失蹤者是張志新(化名)和其女友劉穎下梢,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體塞蹭,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年岗屏,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了漱办。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡暇屋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出咐刨,到底是詐尸還是另有隱情扬霜,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布仔粥,位于F島的核電站,受9級(jí)特大地震影響躯泰,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜麦向,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一诵竭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧卵慰,春花似錦、人聲如沸裳朋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽暖眼。三九已至惕耕,卻和暖如春诫肠,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背惭缰。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工笼才, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人骡送。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓摔踱,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親派敷。 傳聞我的和親對(duì)象是個(gè)殘疾皇子撰洗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

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

  • 1 Pull Up Field(字段上移) 和 Pull Up Method(函數(shù)上移) 將字段或者函數(shù)移動(dòng)到超類...
    hklbird閱讀 615評(píng)論 1 1
  • chapter 1 重構(gòu)差导,第一個(gè)案例 1.1 什么時(shí)候需要重構(gòu) 需要為程序添加一個(gè)特性猪勇,但代碼結(jié)構(gòu)無法使自己方便的...
    VictorBXv閱讀 2,033評(píng)論 0 1
  • 可以先看【推薦】:http://www.reibang.com/p/d6ff54d72afb原文:http://...
    郭某人1閱讀 1,845評(píng)論 0 0
  • 0. 本章內(nèi)容導(dǎo)圖 本章所介紹的重構(gòu)手法專門用來處理類的概括關(guān)系(generalization泣刹,即繼承關(guān)系)。 1...
    塞外的風(fēng)閱讀 399評(píng)論 0 1
  • 《重構(gòu)》讀書筆記 總覽 第一部分 第一章從實(shí)例程序出發(fā)椅您,展示設(shè)計(jì)的缺陷,對(duì)其重構(gòu)可以了解重構(gòu)的過程和方法锥惋。 第二部...
    白樺葉閱讀 2,395評(píng)論 2 5