代码的坏味道
名称 | 说明 | 重构手法 |
---|---|---|
神秘命名 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 {...}
标签:function,重构,return,...,代码,get,笔记,._,class
From: https://www.cnblogs.com/loveshes/p/17367886.html