《重构:改善既有代码的设计》学习笔记

代码的坏味道

名称 说明 重构手法
神秘命名
Mysterious Name
好的命名能够节省时间 改变函数神秘、变量改名、字段改名
重复代码
Duplicated Name
重复代码让人不得不留意其中的细微差异 提炼函数、移动语句、函数上移
过长函数
Long Function
函数越长,就越难理解 提炼函数、以查询取代临时变量、
引入参数对象、保持对象完整、
以命令取代函数、分解条件表达式、
以多态取代条件表达式、拆分循环
过长参数列表
Long Parameter List
使用类缩短参数列表 以查询取代参数、保持对象完整、引入参数对象、移除标记参数、函数组合成类
全局数据
Global Data
难以探测出哪个地方改动了这些数据 封装变量
可变数据
Mutable Data
在一处更新数据,可能会导致另一处得到期望外的数据 封装变量、拆分变量、移动语句、
提炼函数、将查询函数与修改函数分离、
移除设值函数、以查询取代派生变量、
函数组合成类、函数组合成变换、
将引用对象改为值对象
发散式变化
Divergent Change
遇到变化时,需要修改类中的许多地方 拆分阶段、搬移函数、提炼函数、提炼类
霰弹式修改
Shotgun Surgery
遇到某种变化时,需要在不同的类内做出许多小修改 搬移函数、搬移字段、函数组合成类、函数组合成变换、拆分阶段、内联函数、内联类
依恋情节
Feature Envy
一个函数跟另一个模块中的函数或数据交流格外频繁 搬移函数、提炼函数
数据泥团
Data Clumps
成群结队出现的数据,应提炼到一个独立的对象中 提炼类、引入参数对象、保持对象完整
基本类型偏执
Primitive Obsession
创建对自己有用的基本类型(钱、坐标等),而不是使用数字、字符串等简单类型 以对象取代基本类型、以子类取代类型码、以多态取代条件表达式、提炼类、引入参数对象
重复的switch
Repeated Switches
可使用多态消除switch 以多态取代条件表达式
循环语句
Loops
以管道取代循环 以管道取代循环
冗赘的元素
Lazy Element
额外的结构 内联函数、内联类、折叠继承体系
夸夸其谈通用性
Speculate Generality
以各式各样的钩子和特殊情况来处理一些非必要的事情 折叠继承体系、内联函数、内联类、改变函数声明、移除死代码
临时字段
Temporary Field)
内部某个字段仅为某种特定情况而设 提炼类、搬移函数、引入特例
过长的消息链
Message Chains
客户端代码与查找过程中的导航结构紧密结合 隐藏委托关系、提炼函数、搬移函数
中间人
Middle Man
过度使用委托 移除中间人、内联函数、以委托取代超类、以委托取代子类
内幕交易
Insider Trading
模块之间大量交换数据 搬移函数、搬移字段、隐藏委托关系、以委托取代子类、以委托取代超类
过大的类
Large Class
单个类做太多事情,拥有太多字段 提炼类、提炼超类、以子类取代类型码
异曲同工的类
Alternative Classes with Different Interfaces
相似的类 改变函数声明、搬移函数、提炼超类
纯数据类
Data Class
不会说话的容器 封装记录、移除设值函数、搬移函数、提炼函数、拆分阶段
被拒绝的遗赠
Refused Bequest
继承体系设计错误 函数下移、字段下移、以委托取代子类、以委托取代超类
注释
Comments
好的命名能够消除注释 提炼函数、改变函数声明、引入断言

常用重构手段

最常用重构

提炼函数(Extract Function)

反向重构:内联函数

将意图与实现分开;写非常短的函数。

function printOwing(invoice) {
  printBanner();
  let outstanding = calculateOutstanding();
  
  // print details
  console.log(`name: ${invoice.customer}`);
  console.log(`amount: ${outstanding}`);
}

​ ↓

function printOwing(invoice) {
  printBanner();
  let outstanding = calculateOutstanding();
  printDetails(outstanding);
  
  // 函数以“做什么”命名,而不是“怎么做”命名
  function printDetails(outstanding) {
    console.log(`name: ${invoice.customer}`);
  	console.log(`amount: ${outstanding}`);
  }
}

内联函数(Inline Function)

反向重构:提炼函数

去除无用的中间层,直接使用其中的代码。

function getRating(driver) {
  return moreThanFiveLateDeliveries(driver) ? 2 : 1;
}

function moreThanFiveLateDeliveries(driver) {
  return driver.numberOfLateDeliveries > 5;
}

​ ↓

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

提炼变量(Extract Variable)

反向重构:内联变量

提炼变量,用以给表达式命名。

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;

内联变量(Inline Variable)

反向重构:提炼变量

有时候,变量名称并不比表达式本身更具有表现力。

let basePrice = anOrder.basePrice;
return (basePrice > 1000);

​ ↓

return anOrder.basePrice > 1000;

改变函数声明(Change Function Declaration)

修改函数名称用以描述函数的用途。

修改参数列表增加函数的应用范围,去除不必要的耦合。

function circum(radius) {...}

​ ↓

function circumference(radius) {...}

封装变量(Encapsulate Variable)

以函数形式封装对该数据的访问。

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

​ ↓

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

变量改名(Rename Variable)

好的命名是整洁编程的核心,变量可以很好的解释一段程序在干什么。

let a = height * width;

​ ↓

let area = height * width;

引入参数对象(Introduce Parameter Object)

将数据组织成结构。

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

​ ↓

function amountInvoice(aDateRange) {...}
function amountReceived(aDateRange) {...}
function amountOverdue(aDateRange) {...}

函数组合成类(Combine Functions into Class)

将函数组织到一起,给这些函数提供一个共用的环境。(Java没有单独的函数)

function base(aReading) {...}
function taxableCharge(aReading) {...}
function calculateBaseCharge(aReading) {...}

​ ↓

class Reading {
  base() {...}
  taxableCharge() {...}
  calculateBaseCharge() {...}
}

函数组合成变换(Combine Functions into Transform)

将计算派生数据的逻辑收拢到一处,避免到处重复。

function base(aReading) {...}
function taxableChrage(aReading) {...}

​ ↓

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

拆分阶段(Split Phase)

把一大段行为分成顺序执行的几个阶段。

const orderData = orderString.split(/\s+/);
const productPrice = priceList(orderData[0].split("-")[1]);
const orderPrice = parseInt(orderData[1]) * productPrice;

​ ↓

const orderRecord = parseOrder(order);
const orderPrice = price(orderRecord, priceList);

function parseOrder(aString) {
  const values = aString.split(/\s+/);
  return {
    productID: values[0].split("-")[1],
    quantity: parseInt(values[1])
  };
}

function price(order, priceList) {
  return order.quantity * priceList[order.productID]
}

封装

封装记录(Encapsulate Record)

对象可以隐藏结构的细节,让用户不必追究存储的细节和计算的过程。

organization = {name: "Acme Gooseberries", country: "GB"}; // Java中对应Map

​ ↓

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;}
}

封装集合(Encapsulate Collection)

不直接暴露集合本身,提供方法进行修改。

class Person {
  get courses() {return this._courses;}
  set courses(aList) {this._courses = aList;}
}

​ ↓

class Person {
  get courses() {return this._courses.slice();} // 返回副本
  addCourse(aCourse) {...}
  removeCourse(aCourse) {...}
}

以对象取代基本类型(Replace Primitive with Object)

创建新类,提取业务逻辑。

orders.filter(o => "high" === o.priority || "rush" === o.priority);

​ ↓

orders.filter(o => o.priority.higherThan(new Priority("normal")));

以查询取代临时变量(Replace Temp with Query)

将临时变量抽取到函数

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

​ ↓

get basePrice() {this._quantity * this._itemPrice;} // 语法糖,变量名叫 basePrice
...
if (this.basePrice > 1000) {
  return this.basePrice * 0.95;
} else {
  return this.basePrice * 0.98;
}

提炼类(Extract Class)

反向重构:内联类

一个类应该是一个清晰的抽象,只处理一些明确的责任。

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;}
}

内联类(Inline Class)

反向重构:提炼类

将不再承担足够责任的类(萎缩类)塞进另一个类中。

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;}
}

隐藏委托关系(Hide Delegate)

反向重构:移除中间人

通过委托函数去除委托关系这种依赖,隐藏实现细节。

manager = aPerson.department.manager;

​ ↓

manager = aPerson.manager;

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

移除中间人(Remove Middle Man)

反向重构:隐藏委托关系

去除过多的转发函数,让客户直接调用受托类。

manager = aPerson.manager;

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

​ ↓

manager = aPerson.department.manager;

替换算法 (Substitute Algorithm)

使用更简单更清晰的方式。

function foundPerson(people) {
  for (let i=0; i<people.length; i++) {
    if (people[i] === "Don") {
      return "Don";
    }
    if (people[i] === "John") {
      return "John";
    }
    if (people[i] === "Kent") {
      return "Kent";
    }
  }
  return "";
}

​ ↓

function foundPerson(people) {
  const candidates = ["Don", "John", "Kent"];
  return people.find(p => candidates.includes(p)) || '';
}

搬移特性

搬移函数(Move Function)

让函数处于更亲近的上下文(类)中。

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

​ ↓

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

搬移字段(Move Field)

相关联的数据应处于同一个数据结构中。

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

​ ↓

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

搬移语句到函数(Move Statements into Function)

反向重构:搬移语句到调用者

将相同的代码合并到函数里面。

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

function photoData(aPhtot) {
  return [
    `<p>location: ${aPhoto.location}</p>`,
    `<p>date: ${aPhoto.date.toDateString()}</p>`,
  ];
}

​ ↓

result.concat(photoData(person.photo));

function photoData(aPhtot) {
  return [
    `<p>title: ${person.photo.title}</p>`,
    `<p>location: ${aPhoto.location}</p>`,
    `<p>date: ${aPhoto.date.toDateString()}</p>`,
  ];
}

搬移语句到调用者(Move Statements to Callers)

反向重构:搬移语句到函数

系统演进导致抽象边界发生偏移时,将表现不同的行为搬移到调用点。亦或是收拢重复逻辑。

emitPhotoData(outStream, person.photo);

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

​ ↓

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

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

以函数调用取代内联代码(Replace Inline Code with Function Call)

使用函数将相关的行为打包起来,提升代码的表达力。

let appliesToMass = false;
for (const s of states) {
  if (s === "MA") {
    appliesToMass = true;
  }
}

​ ↓

appliesToMass = states.includes("MA");

移动语句(Slide Statements)

让存在关联的东西一起出现,使代码更容易理解。

const pricingPlan = retrievePricingPlan();
const order = retreiveOrder();
let charge;
const chargeResult = pricingPlan.uint;

​ ↓

const pricingPlan = retrievePricingPlan();
const chargeResult = pricingPlan.uint;
const order = retreiveOrder();
let charge;

拆分循环(Split Loop)

一个循环只做一件事情。

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

​ ↓

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

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

​ (更进一步)↓

function totalSalary(people) {
  return people.reduce((total, p) => total + p.salary, 0)
}

function averageAge(people) {
  return people.reduce((total, p) => total + p.age, 0) / people.length;
}

以管道取代循环()

利用语言特性,提高代码的可读性。

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);

移除死代码(Remove Dead Code)

一旦代码不再被使用,就该立马删除它。即使可能再度启用,也有版本控制系统可以找到。

if (false) {
  doSomethingThatUsedToMatter();
}

​ ↓


重新组织数据

拆分变量(Split Variable)

如果变量承担多个责任,就应该被分解为多个变量。

let temp = 2 * (height + width);
console.log(temp);
temp = heigh * width;
console.log(temp);

​ ↓

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

字段改名(Rename Field)

好的命名有助于理解。

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

​ ↓

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

以查询取代派生变量(Replace Derived Variable with Query)

有些变量其实可以很容易地随时计算出来。

get discountedTotal() {return this._discountedTotal;}
set discount(aNumber) {
  const old = this._discount;
  this._discount = aNumber;
  this._discountedTotal += old - aNumber;
}

​ ↓

get discountedTotal() {return this._baseTotal - this._discount;}
set discount(aNumber) {
  this._discount = aNumber;
}

将引用对象改为值对象(Change Reference to Value)

反向重构:将值对象改为引用对象

将内部对象视作不可变,修改属性值时,替换整个内部对象。

class Product {
  applyDiscount(arg) {this._price.amount -= arg;}
}

​ ↓

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

将值对象改为引用对象(Change Value to Reference)

反向重构:将引用对象改为值对象

将多份副本数据变成单一的引用,方便更新数据。

let customer = new Customer(customerData);

​ ↓

let customer = customerRepository.get(customerData.id);

简化条件逻辑

分解条件表达式(Decompose Conditional)

分解成多个独立的函数,突出条件逻辑,更清楚地表明每个分支的作用。

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

​ ↓

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

合并条件表达式(Consolidate Conditional Expression)

同一次检查(检查条件各不相同,最终行为却一致),应该合并成一个条件表达式。

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

​ ↓

if (isNotEligibleForDisability()) return 0;

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

以卫语句取代嵌套表达式(Replace Nested Conditional with Guard Clauses)

如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回。

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

​ ↓

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

⭐️以多态取代条件表达式(Replace Conditional with Polymorphism)

将条件逻辑拆分到不同的场景。(P.272)

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

​ ↓

class EnropeanSwallow {
  get plumage() {
    return "average";
  }
}

class AfricanSwallow {
  get plumage() {
    return (bird.numberOfCoconuts > 2) ? "tired" : "average";
  } 
}

class NorwegianBlueParrot {
  get plumage() {
    return (bird.voltage > 100) ? "scorched" : "beautiful";
  } 
}

引入特例(Introduce Special Case)

创建一个特例元素,用以表达对这种特例的共用行为的处理。

if (aCustomer === "unknown") customerName = "occupant";

​ ↓

class UnknownCustomer {
  get name() {return "occupant";}
}

引入断言(Introduce Assertion)

使用断言来检查“必须为真”的条件,而不是“我认为应该为真”的条件。

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

​ ↓

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

重构API

将查询函数和修改函数分离(Separate Query from Modifier)

任何有返回值的函数,都不应该有看得到的副作用。

function getTotalOustandingAndSendBill() {
  const result = custorer.inovices.reduce((total, each) => each.amount + total, 0);
  sendBill();
  return result;
}

​ ↓

function totalOustanding() {
  return result = custorer.inovices.reduce((total, each) => each.amount + total, 0);
}

function sendBill() {
  emailGateway.send(formatBill(customer));
}

函数参数化(Parameterize Function)

合并逻辑相同但字面量值不同的函数,消除重复。

function tenPercentRaise(aPerson) {
  aPerson.salary = aPerson.salary.multiply(1.1);
}

function fivePercentRaise(aPerson) {
  aPerson.salary = aPerson.salary.multiply(1.05);
}

​ ↓

function raise(aPerson, factor) {
  aPerson.salary = aPerson.salary.multiply(1 + factor);
}

移除标记参数(Remove Flag Argument)

明确函数的调用逻辑

function bookConcert(aCustomer, isPremium) {
  if (isPremium) {
    // logic for premium booking
  } else {
    // logic for regular booking
  }
}

​ ↓

function premiumBookConcert(aCustomer) {...}
function regularBookConcert(aCustomer) {...}

保持对象完整(Preserve Whole Object)

传递整个记录,而不是从记录中导出几个值又一起传递给另一个函数。

const low = aRoom.daysTempRange.low;
const high = aRoom.daysTempRange.high;
if (aPlan.withinRange(low, high)) {...}

​ ↓

if (aPlan.withinRange(aRoom.daysTempRange)) {...}

以查询取代参数(Replace Parameter with Query)

反向重构:以参数取代查询

去掉本不必要的参数。

availableVacation(anEmployee, anEmployee.grade);

function availableVacation(anEmployee, grade) {
  // calculate vacation
}

​ ↓

availableVacation(anEmployee);

function availableVacation(anEmployee, grade) {
  const grade = anEmployee.grade;
  // calculate vacation
}

以参数取代查询(Replace Query with Parameter )

反向重构:以查询取代参数

去除令人不快的依赖引用关系,提纯函数。

targetTemperature(aPlan);

function targetTemperature(aPlan) {
  currentTemperature = thermostat.currentTemperature;
  // rest of function
}

​ ↓

targetTemperature(aPlan, thermostat.currentTemperature);

function targetTemperature(aPlan, currentTemperature) {
  // rest of function
}

移除设值函数(Remove Setting Method)

不需要被修改的字段不要提供设值函数。

class Person {
  get name() {...}
  set name(aString) {...}
}

​ ↓

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

以工厂函数取代构造函数(Replace Constructor with Factory Function)

更方便的初始化对象方式。

leadEngineer = new Employee(document.leadEngineer, 'E');

​ ↓

leadEngineer = createEngineer(document.leadEngineer);

以命令取代函数(Replace Function with Command)

反向重构:以函数取代命令

命令对象相比普通函数有更丰富的生命周期管理能力。

function score(candidate, medicalExam, scoringGuide) {
  let result = 0;
  let healthLevel = 0;
  // long body code
}

​ ↓

class Scorer {
  constructor(candidate, medicalExam, scoringGuide) {
    this._candidate = candidate;
    this._medicalExam = medicalExam;
    this._scoringGuide = scoringGuide;
  }
  
  execute() {
    this._result = 0;
    this._healthLevel = 0;
    // long body code
  }
}

以函数取代命令(Replace Command with Function)

反向重构:以命令取代函数

函数不是很复杂时,使用命令对象太繁琐。

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;
}

处理继承关系

函数上移(Pull Up Method)

反向重构:函数下移

避免重复代码。

class Employee {...}

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

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

​ ↓

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

字段上移(Pull Up Field)

反向重构:字段下移

去除重复的字段。

class Employee {...} // Java
  
class Salesman extends Employee {
	private String name;
}

class Engineer extends Employee {
  private String name;
}

​ ↓

class Employee {
  protected String name;
}
  
class Salesman extends Employee {...}

class Engineer extends Employee {...}

构造函数本体上移(Pull Up Constructor Body)

提炼子类中共有的构造行为。

class Party {...}

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

​ ↓

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

class Employee extends Party {
  constructor(name, id, monthlyCost) {
    super(name);
    this._id = id;
    this._monthlyCost = monthlyCost;
  }
}

函数下移(Push Down Method)

反向重构:函数上移

将只与一个(或少数几个)子类相关的函数从超类中移走。

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

​ ↓

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

字段下移(Push Down Field)

反向重构:字段上移

将只被一个子类(或一小部分子类)用到的字段从超类中移走。

class Employee { // Java
  protected String quota;
}
  
class Salesman extends Employee {...}
class Engineer extends Employee {...}

​ ↓

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

class Salesman extends Employee {
	private String quota;
}

以子类取代类型码(Replace Type Code with Subclasses)

反向重构:移除子类

子类能够更明确地表达数据与类型之间的关系。

function createEmployee(name, type) {
  return new Employee(name, type);
}

​ ↓

function createEmployee(name, type) {
  swith (type) {
    case "engineer": return new Engineer(name);
    case "salesman": return new Salesman(name);
    case "manager": return new Manager(name);
  }
}

移除子类(Remove Subclass)

反向重构:以子类取代类型码

子类的用处太少,不值得存在时替换成超类中的一个字段。

class Person {
  get genderCode() {return "X";}
}

class Male extends Person {
  get genderCode() {return "M";}
}

class Female extends Person {
  get genderCode() {return "F";}
}

​ ↓

class Person {
  get genderCode() {return this._genderCode;}
}

提取超类(Extract Superclass)

利用基本的继承机制将不同类的相似之处提炼到超类。

class Department {
  get totalAnnualCost() {...}
  get name() {...}
  get headCount() {...}
}
              
class Employee {
  get annualCost() {...}
  get name() {...}
  get id() {...}
}

​ ↓

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

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

折叠继承体系(Collapse Hierarchy)

子类与超类没有多大差别,不值得再作为独立的类存在。

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

​ ↓

class Employee {...}

以委托取代子类(Replace Subclass with Delegate)

继承只能使用一次,对于不同的分类,使用委托。

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

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

​ ↓

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

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

以委托取代超类(Replace Superclass with Delegate)

继承会暴露不需要的超类中的接口。

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

​ ↓

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

class List {...}
posted @ 2023-05-02 16:40  爱Code的王饱饱  阅读(144)  评论(0编辑  收藏  举报