美文网首页重构
《重构》- 简化函数调用

《重构》- 简化函数调用

作者: nimw | 来源:发表于2018-11-20 20:05 被阅读4次

一. Rename Method(函数改名)

介绍

  1. 场景
    函数的名称未能揭示函数的用途。
  2. 手法
    修改函数名称。

动机

  1. 给函数命名有一个好方法:首先考虑应该给这个函数写上一句怎样的注释,然后想办法将注释编程函数名称。
  2. 如果你看到一个函数名称不能很好地传达它的用途,应该马上加以修改。
  3. 你的代码首先是为人写的,其次才是为计算机写的。而人需要良好名称的函数。

范例

重构前

getTelephoneNumber (){
  return `(${_officeAreaCode}-${_officeNumber})`
}

重构后

getOfficeTelephoneNumber (){
  return `(${_officeAreaCode}-${_officeNumber})`
}

二. Add Parameter(添加参数)

介绍

  1. 场景
    某个函数需要从调用端得到更多信息。
  2. 手法
    为此函数添加一个对象参数,让该对象带进函数所需信息。

动机

  1. 除了添加参数外,你常常还有其他选择。只要可能,其他选择都比添加参数要好,因为他们不会增加参数列的长度。
  2. 过长的参数列是不好的味道,因为程序员很难记住那么多参数。
  3. 并非禁止添加参数,但是在添加参数之前需要了解是否有其他选择。

三. Remove Parameter(移除参数)

介绍

  1. 场景
    函数本体不再需要某个参数。
  2. 手法
    将该参数去除。

动机

  1. 程序员可能经常添加参数,却往往不愿意去掉他们。
  2. 参数代表着函数所需的信息,不同的参数值有不同的意义,应及时去掉多余参数。

四. Separate Query form Modifier(将查询函数和修改函数分离)

介绍

  1. 场景
    某个函数既返回对象状态值,又修改对象状态。
  2. 手法
    建立两个不同的函数,其中一个负责查询,另一个负责修改。

动机

  1. 任何有返回值的函数,都不应该有看得到的副作用。
  2. 如果你遇到一个“既有返回值又有副作用”的函数,就应该试着将查询动作从修改动作中分割出来。

范例

重构前

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(令函数携带参数)

介绍

  1. 场景
    若干函数做了类似的工作,但在函数本体中却包含了不同的值。
  2. 手法
    建立单一函数,以参数表达那些不同的值。

动机

  1. 你可能发现这样的两个函数:他们做着类似的工作,但因为少数几个值致使行为略有不同。
  2. 你可以将这些各自分离的函数统一起来,并通过参数来处理那些变化,用以简化问题。
  3. 本项重构的要点在于:以“可将少量数据视为参数”为依据,找出带有重复性的代码。

范例

重构前

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(以明确函数取代参数)

介绍

  1. 场景
    你有一个函数,其中完全取决于参数值而采取不同行为。
  2. 手法
    针对该参数的每一个可能值,建立一个独立函数。

动机

  1. 如果某个参数有多种可能的值,而函数内又以条件表达式检查这些参数值,并根据不同参数值做出不同的行为,那么就应该使用本项重构。
  2. 提供不同的函数给调用者使用,可以避免出现条件表达式。
  3. 本项重构可以获取一个更清晰的接口,哪怕只是给一个内部的布尔变量赋值,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(保持对象完整)

介绍

  1. 场景
    你从某个对象中取出若干值,将他们作为某一次函数调用时的参数。
  2. 手法
    改为传递整个对象。

范例

重构前

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(以函数取代参数)

介绍

  1. 场景
    对象调用某个函数,并将所得结果作为参数,传递给另一个函数。而接受该参数的函数本身也能够调用前一个函数。
  2. 手法
    让参数接受者去除该项参数,并直接调用前一个函数。

动机

  1. 如果函数可以通过其他途径获得参数值,那么他就不应该通过参数获取该值。

范例

重构前

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(引入参数对象)

介绍

  1. 场景
    某些参数总是很自然地同时出现。
  2. 手法
    以一个对象取代这些参数。

动机

  1. 经常看到特定的一组参数总是一起被传递。可能有好几个函数都使用这一组参数,这些函数可能隶属与同一个类,也可能隶属于不同的类。
  2. 这样的一组参数就是所谓的数据泥团,我们可以运用一个对象包装所有这些数据,再以该对象取代他们。

范例

重构前

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(移除设置函数)

介绍

  1. 场景
    类中的某个字段应该在对象创建时被设值,然后就不再改变。
  2. 手法
    去掉该字段的所有设值函数。

范例

重构前

class Account {
  constructor(id) {
    this.setId(id)
  }

  setId(arg) {
    this._id = arg
  }
}

重构后

class Account {
  constructor(id) {
    this._id = id
  }
}

十一. Hide Method(隐藏函数)

介绍

  1. 场景
    有一个函数,从来没有被其他任何类用到。
  2. 手法
    将这个函数修改为private

动机

  1. 重构往往促使你修改函数的可见度。
  2. 当你面对一个过于丰富、提供了过多行为的接口时,就值得将非必要的取值函数和设值函数隐藏起来。

十二. Replace Constructor with Factory Method(以工厂函数取代构造函数)

介绍

  1. 场景
    你希望在创建对象时不仅仅是做简单的建构工作。
  2. 手法
    将构造函数替换为工厂函数。

范例

重构前

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(封装向下转型)

介绍

  1. 场景
    某个函数返回的对象,需要由函数调用者执行向下转型。
  2. 手法
    将向下转型动作移至函数中。

范例

JavaScript无需转型,无法演示该重构手法。

十四. Replace Error Code with Exception(以异常取代错误码)

介绍

  1. 场景
    某个函数返回一个特定的代码,用以表示某种错误情况。
  2. 手法
    改用异常。

范例

重构前

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(以测试取代异常)

介绍

  1. 场景
    面对一个调用者可以预先检查的条件,你抛出了一个异常。
  2. 手法
    修改调用者,使它在调用函数之前先做检查。

动机

  1. 异常只应该被用于异常的、罕见的行为,也就是那些产生意料之外的错误的行为。而不应该成为条件检查的替代品。

范例

重构前

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

相关文章

  • 《重构》- 简化函数调用

    一. Rename Method(函数改名) 介绍 场景函数的名称未能揭示函数的用途。 手法修改函数名称。 动机 ...

  • 重构——简化函数调用

    1 Rename Method(函数改名) 修改函数名字以良好表达函数的用途。Motivation:各种大师特别推...

  • 《重构》学习笔记(08)-- 简化函数调用

    本章主要针对的是对函数的重构,包括函数改名,参数增删等。良好的函数调用可以增加代码的可读性和可维护性。 Renam...

  • 重构读书笔记-10总结

    重构第十章 总结 本章主要讲解了如何简化函数的调用,开发出更加容易理解和容易使用的接口的重构方法 1.Rename...

  • 简化函数调用

    0. 本章内容导图 在对象技术中,最重要的概念莫过于“接口”,容易被理解和被使用的接口是开发良好面向对象软件的关键...

  • 简化函数调用

    一 Rename Method(修改函数名字) 怎么说 修改函数的名字,让函数名来揭示函数的用途 为什么 我们应该...

  • 第十章 简化函数调用

    简化函数调用 10.1 Rename Method (函数改名) 函数的名称未能揭示函数的用途 修改函数名称 将复...

  • 重构读书笔记-10_8-Replace_Parameter_wi

    重构第十章 8.Replace Parameter with Methods(以函数取代参数) 对象调用某个函数,...

  • pthreads-tutorial

    4.1 重构 3: 封装 -> 实现出 简化版 barrier 的 数据结构 + 3个函数 1 First PT...

  • Java函数式编程实战之策略工厂

    前言 Java 8 函数式编程可以简化传统设计模式使用方式,下面看两个重构例子,用Functional Inter...

网友评论

    本文标题:《重构》- 简化函数调用

    本文链接:https://www.haomeiwen.com/subject/cvxfqqtx.html