首页 > 其他分享 >《重构:改善既有代码的设计》学习笔记

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

时间:2023-05-02 16:44:05浏览次数:44  
标签:function 重构 return ... 代码 get 笔记 ._ class

代码的坏味道

名称 说明 重构手法
神秘命名
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

相关文章

  • 点分治学习笔记
    点分治序列上的操作可以用很多方式维护,线段树树状数组平衡树啥的,但是如果毒瘤出题人把序列搬到了树上,则需要一些奇妙方法。一般有两种(好几种?)方法:树链剖分,把树上路径转化成dfn序上的多段进行操作。LCT,不多说,目前只会板子,没搞太懂。点分治,这个是不用把树转化成序列的,而是将树......
  • Qt 学习笔记
     1.  *newClass 与引用<qpushbutton.cpp>:QPushButton::QPushButton(QWidget*parent):QAbstractButton(*newQPushButtonPrivate,parent){Q_D(QPushButton);d->init();}<qabstractbutton.cpp>:/*!\internal*/对应的函数原型......
  • IT工具知识-18: ADB操作笔记(自用)
    Linux下的常用命令(持续更新)终端使用bashshell查询安卓设备当前活动的APP包名和活动窗口名adbshelldumpsyswindowwindows|grep-E'mCurrentFocus|mFocusedApp'启动指定app下的指定窗口app包名和活动窗口名都要提供,否则无法启动adbshellamstart包名/活动窗口......
  • unity发布到4399的webgl模式问题:FRAMEWORK.JS中的WEBREQUEST_SEND括号内的函数(不能有
    在发布4399的时候,之前遇到过这个问题,解决方法当然就是删除这个函数啦。步骤也很简单,但是刚开始摸不着头脑搞了好久,最后发现发布的时候有个加密选项,选择不加密,后面build的文件里面就可以进行打开修改,按照要求修改函数即可。......
  • 四月读书笔记
    梦断代码这本书让我越发意识到作为软件开发者的不容易。程序员都怀揣着成就一番事业的心,他们信心满满,斗志昂扬,但因为种种私人原因不能够与其他程序员很好的合作,团队精神难以成型。作为乐观主义者,他们不畏惧任何困难,正因如此,才为计算机提供了无尽的可能目标要实际。实际这个词其实......
  • 医学图像的深度学习的完整代码示例:使用Pytorch对MRI脑扫描的图像进行分割
    图像分割是医学图像分析中最重要的任务之一,在许多临床应用中往往是第一步也是最关键的一步。在脑MRI分析中,图像分割通常用于测量和可视化解剖结构,分析大脑变化,描绘病理区域以及手术计划和图像引导干预,分割是大多数形态学分析的先决条件。本文我们将介绍如何使用QuickNAT对人脑的......
  • 软件开发人员短缺,低代码等新技术或成解决途径
    导读Reveal发布的一份2023年度软件开发人员调查报告指出,开发人员短缺已经连续第二年成为行业最大挑战。该报告基于对2000多名软件开发人员和IT专业人员的调查数据。超三分之一的受访者(37.5%)表示,掌握熟练技能的开发人员在2023年将非常紧缺,其中DevOps工程师......
  • 蓝桥杯刷题笔记
    0杂//ASCII码数字-48A=65a=97//字符串分割//从下标0开始取n-1个字符str=str.substr(0,n-1)//二维vector的添加数据以及遍历vector<vector<int>>v;for(inti=0;i<2;i++){ vector<int>tmp; for(intj=0;j<2;j++) { tmp.push_back(j); } v.pu......
  • Treap 学习笔记
    一、TreapTreap是一种通过旋转操作维护性质的二叉搜索树。定义详见要维护的东西还是一样,对于每个节点,要维护它的左右儿子,子树大小,还有权值和随机的优先级(这样才能保证树的高度是\(O(\logn)\)级别的)。注意:旋转、分裂、伸展什么的都是手段,维持平衡树的2个性质才是目的。......
  • Quixel Mixer学习笔记:软件入门使用
    本随笔用于记录随笔作者在学习使用纹理和材质制作软件QuixelMixer时学到的知识点,属于入门级别的笔记。本随笔使用的QuixelMixer版本为2022.1.1Beta,内容整理自官方手册。随笔作者还处在学习阶段,在软件的使用和理解还不够透彻,难免在技术上或书写上出现问题,如出现类似的问题欢迎......