一. Rename Method(函数改名)
介绍
- 场景
函数的名称未能揭示函数的用途。 - 手法
修改函数名称。
动机
- 给函数命名有一个好方法:首先考虑应该给这个函数写上一句怎样的注释,然后想办法将注释编程函数名称。
- 如果你看到一个函数名称不能很好地传达它的用途,应该马上加以修改。
- 你的代码首先是为人写的,其次才是为计算机写的。而人需要良好名称的函数。
范例
重构前
getTelephoneNumber (){
return `(${_officeAreaCode}-${_officeNumber})`
}
重构后
getOfficeTelephoneNumber (){
return `(${_officeAreaCode}-${_officeNumber})`
}
二. Add Parameter(添加参数)
介绍
- 场景
某个函数需要从调用端得到更多信息。 - 手法
为此函数添加一个对象参数,让该对象带进函数所需信息。
动机
- 除了添加参数外,你常常还有其他选择。只要可能,其他选择都比添加参数要好,因为他们不会增加参数列的长度。
- 过长的参数列是不好的味道,因为程序员很难记住那么多参数。
- 并非禁止添加参数,但是在添加参数之前需要了解是否有其他选择。
三. Remove Parameter(移除参数)
介绍
- 场景
函数本体不再需要某个参数。 - 手法
将该参数去除。
动机
- 程序员可能经常添加参数,却往往不愿意去掉他们。
- 参数代表着函数所需的信息,不同的参数值有不同的意义,应及时去掉多余参数。
四. Separate Query form Modifier(将查询函数和修改函数分离)
介绍
- 场景
某个函数既返回对象状态值,又修改对象状态。 - 手法
建立两个不同的函数,其中一个负责查询,另一个负责修改。
动机
- 任何有返回值的函数,都不应该有看得到的副作用。
- 如果你遇到一个“既有返回值又有副作用”的函数,就应该试着将查询动作从修改动作中分割出来。
范例
重构前
foundMiscreant(people) {
for(let i = 0; i < people.length; i++) {
if(people[i] === 'Don' || peoplep[i] === 'John') {
this.sendAlert()
return 'Dom'
}
}
return ''
}
checkSecurity(people) {
const found = this.foundMiscreant(people)
this.someLaterCode(found)
}
重构后
foundPerson(people) {
for(let i = 0; i < people.length; i++) {
if(people[i] === 'Don' || peoplep[i] === 'John') {
return 'Dom'
}
}
return ''
}
alertPerson(people) {
if(this.foundPerson(people)) {
this.sendAlert()
}
}
checkSecurity(people) {
this.alertPerson(people)
const found = this.foundPerson(people)
this.someLaterCode(found)
}
五. Parameterize Method(令函数携带参数)
介绍
- 场景
若干函数做了类似的工作,但在函数本体中却包含了不同的值。 - 手法
建立单一函数,以参数表达那些不同的值。
动机
- 你可能发现这样的两个函数:他们做着类似的工作,但因为少数几个值致使行为略有不同。
- 你可以将这些各自分离的函数统一起来,并通过参数来处理那些变化,用以简化问题。
- 本项重构的要点在于:以“可将少量数据视为参数”为依据,找出带有重复性的代码。
范例
重构前
baseCharge() {
let result = Math.min(this.lastUsage(), 100) * 0.03
if(this.lastUsage() > 100) {
result += (Math.min(this.lastUsage(), 200) -100) * 0.05
}
if(this.lastUsage() > 200) {
result += (this.lastUsage() - 200) * 0.07
}
return new Dollars(result)
}
重构后
baseCharge() {
let result = this.usageInRange(0, 100) * 0.03
result += this.usageInRange(100, 200) + 0.05
result += this.usageInRange(200, Number.MAX_SAFE_INTEGER)
return new Dollars(result)
}
usageInRange(start, end) {
return this.lastUsage() > start ? Math.min(this.lastUsage(), end) - start : 0
}
六. Replace Parameter with Explicit Methods(以明确函数取代参数)
介绍
- 场景
你有一个函数,其中完全取决于参数值而采取不同行为。 - 手法
针对该参数的每一个可能值,建立一个独立函数。
动机
- 如果某个参数有多种可能的值,而函数内又以条件表达式检查这些参数值,并根据不同参数值做出不同的行为,那么就应该使用本项重构。
- 提供不同的函数给调用者使用,可以避免出现条件表达式。
- 本项重构可以获取一个更清晰的接口,哪怕只是给一个内部的布尔变量赋值,
Switch.beOn()
也比Switch.setState(true)
要清晰的多。
范例
重构前
class Employee {
static ENGINEER = 0;
static SALESMAN = 1;
static MANAGER = 2;
static create(type) {
switch(type) {
case Employee.ENGINEER:
return new Engineer()
case Employee.SALESMAN:
return new Salesman()
case Employee.MANAGER:
return new Manager()
default:
throw new Error('Incorrect type value')
}
}
}
const e = Employee.create(Employee.ENGINEER)
重构后
class Employee {
static createEngineer() {
return new Engineer()
}
static createSalesman() {
return new Salesman()
}
static createManager() {
return new Manager()
}
}
const e = Employee.createEngineer()
七. Preserve Whole Object(保持对象完整)
介绍
- 场景
你从某个对象中取出若干值,将他们作为某一次函数调用时的参数。 - 手法
改为传递整个对象。
范例
重构前
class Room {
withinPlan(plan) {
const low = this.daysTempRange().getLow()
const high = this.daysTempRange().getHigh()
return plan.withinRange(low, high)
}
}
class HeatingPlan {
withinRange(low, high) {
return low >= this._range.getLow() && high <= this._range.getHigh()
}
}
重构后
class Room {
withinPlan(plan) {
return plan.withinRange(this.daysTempRange())
}
}
class HeatingPlan {
withinRange(arg) {
return arg.getLow() >= this._range.getLow() && arg.getHigh() <= this._range.getHigh()
}
}
八. Replace Parameter with Methods(以函数取代参数)
介绍
- 场景
对象调用某个函数,并将所得结果作为参数,传递给另一个函数。而接受该参数的函数本身也能够调用前一个函数。 - 手法
让参数接受者去除该项参数,并直接调用前一个函数。
动机
- 如果函数可以通过其他途径获得参数值,那么他就不应该通过参数获取该值。
范例
重构前
getPrice() {
const basePrice = this._quantity * this._itemPrice
const discountLevel = this._quantity > 100 ? 2 : 1
return this.discountPrice(basePrice, discountLevel)
}
discountPrice(basePrice, discountLevel) {
return discountLevel === 2 ? basePrice * 0.1 : basePrice * 0.05
}
重构后
getPrice() {
return this.getDiscountLevel() === 2 ? this.getBasePrice() * 0.1 : this.getBasePrice() * 0.05
}
getBasePrice() {
return this._quantity * this._itemPrice
}
getDiscountLevel() {
return this._quantity > 100 ? 2 : 1
}
九. Introduce Parameter Object(引入参数对象)
介绍
- 场景
某些参数总是很自然地同时出现。 - 手法
以一个对象取代这些参数。
动机
- 经常看到特定的一组参数总是一起被传递。可能有好几个函数都使用这一组参数,这些函数可能隶属与同一个类,也可能隶属于不同的类。
- 这样的一组参数就是所谓的数据泥团,我们可以运用一个对象包装所有这些数据,再以该对象取代他们。
范例
重构前
class Entry{
constructor(value, chargeDate) {
this._value = value
this._chargeDate = chargeDate
}
getDate() {
return this._chargeDate
}
getValue() {
return this._value
}
}
class Account{
getFlowBetween(start, end) {
let result = 0
this._entries.forEach(entry => {
if(entry.getDate().equals(start) || entry.getDate().equals(end) || (entry.getDate().after(start) && entry.getDate().before(end))) {
result += entry.getValue()
}
})
return result
}
}
const flow = anAccount.getFlowBetween(startDate, endDate)
重构后
class Entry{
constructor(value, chargeDate) {
this._value = value
this._chargeDate = chargeDate
}
getDate() {
return this._chargeDate
}
getValue() {
return this._value
}
}
class DateRange {
constructor(start, end) {
this._start = start
this._end = end
}
getStart() {
return this._start
}
getEnd() {
return this._end
}
includes(arg) {
return arg.equals(this._start) ||
arg.equals(this._end) ||
(arg.after(this._start) && arg.before(this._end))
}
}
class Account{
getFlowBetween(range) {
let result = 0
this._entries.forEach(entry => {
if(range.includes(entry.getDate())){
result += entry.getValue()
}
})
return result
}
}
const flow = anAccount.getFlowBetween(new DateRange(startDate, endDate))
十. Remove Setting Method(移除设置函数)
介绍
- 场景
类中的某个字段应该在对象创建时被设值,然后就不再改变。 - 手法
去掉该字段的所有设值函数。
范例
重构前
class Account {
constructor(id) {
this.setId(id)
}
setId(arg) {
this._id = arg
}
}
重构后
class Account {
constructor(id) {
this._id = id
}
}
十一. Hide Method(隐藏函数)
介绍
- 场景
有一个函数,从来没有被其他任何类用到。 - 手法
将这个函数修改为private
动机
- 重构往往促使你修改函数的可见度。
- 当你面对一个过于丰富、提供了过多行为的接口时,就值得将非必要的取值函数和设值函数隐藏起来。
十二. Replace Constructor with Factory Method(以工厂函数取代构造函数)
介绍
- 场景
你希望在创建对象时不仅仅是做简单的建构工作。 - 手法
将构造函数替换为工厂函数。
范例
重构前
class Employee {
static ENGINEER = 0;
static SALESMAN = 1;
static MANAGER = 2;
constructor(type) {
this._type = type
}
}
重构后
class Employee {
static ENGINEER = 0;
static SALESMAN = 1;
static MANAGER = 2;
static create(type) {
return new Employee(type)
}
constructor(type) {
this._type = type
}
}
十三. Encapsulate Downcast(封装向下转型)
介绍
- 场景
某个函数返回的对象,需要由函数调用者执行向下转型。 - 手法
将向下转型动作移至函数中。
范例
JavaScript
无需转型,无法演示该重构手法。
十四. Replace Error Code with Exception(以异常取代错误码)
介绍
- 场景
某个函数返回一个特定的代码,用以表示某种错误情况。 - 手法
改用异常。
范例
重构前
withdraw(amount){
if(mount > this._balance) {
return -1
} else {
this._balance -= amount
return 0
}
}
重构后
withdraw(amount){
if(mount > this._balance) {
throw new Error('余额不足')
}
this._balance -= amount
}
十五. Replace Exception with Test(以测试取代异常)
介绍
- 场景
面对一个调用者可以预先检查的条件,你抛出了一个异常。 - 手法
修改调用者,使它在调用函数之前先做检查。
动机
- 异常只应该被用于异常的、罕见的行为,也就是那些产生意料之外的错误的行为。而不应该成为条件检查的替代品。
范例
重构前
class ResourcePool {
_available;
_allocated;
getResource() {
let result;
try {
result = this._available.pop()
this._allocated.push(result)
return result
} catch (error) {
result = new Resource()
this._allocated.push(result)
return result
}
}
}
重构后
class ResourcePool {
_available;
_allocated;
getResource() {
let result;
if(this._available.isEmpty()) {
result = new Resource()
} else {
result = this._available.pop()
}
this._allocated.push(result)
return result
}
}
网友评论