- 重构前,先检查自己是否有一套可靠的测试集。这些测试必须有自我验证能力。TDD
- 重构技术就是以微小的步伐修改程序。如果犯下错误,很容易便可发现它。
- 傻瓜都能写出计算机可以理解的代码。唯有能写出人类容易理解的代码的,才是优秀的程序员。
- 编程时,需要遵循营地法则:保证你离开时,代码库一定比来的时候更健康。
- 好代码验证的标准是人们是否能轻而易举的修改它。
- 重构:对软件内部结构的一种调整,目的是在不改变可观察行为的前提下,提高其可理解性,降低其修改成本。
- 重构的关键在于运用大量小且保证软件行为的步骤,一步步达到大规模的修改。
- 如果有人说他们的代码在重构过程中有1-2天时间不可用,基本上可以确定,他们在做的事不是重构。
- 添加新功能,可能需要优化之前的程序结构;当功能开发好,也需要优化下程序的结构。不同的角色切换,是在添加功能过程中必不可少的步骤。
- 改进软件的设计
- 使软件更容易理解
- 帮助开发者找到bug
- 提高编程速度
- 预备性:让添加新功能更容易
- 帮助理解:使代码更易懂
- 捡垃圾式重构
- 有计划和见机行事的重构:肮脏的代码必须重构,但漂亮的代码也需要很多重构
- 长期重构
- 复审代码时重构
- 何时不应该重构:
- 只有当需要理解其工作原理时
- 如果重写比重构容易。
- 缓解新功能开发
- 重构的唯一目的就是让我们开发更快,用更少的工作量创造更大的价值
- 重构应该总是由经济利益驱动,而不是在于把代码库打磨得闪闪发光
-
目的:简化条件,易于理解代码逻辑
-
场景:当检查处理逻辑复杂时
-
例子:
if(!date.isBefore(plan.summerStart) && !date.isAfter(plan.summerEnd)){ charge = quanlity * plan.summerRate; }else{ charge = quanlity * plan.regularRate + plan.regularServiceCharge; }
const isSummer = !date.isBefore(plan.summerStart) && !date.isAfter(plan.summerEnd); function summerCharge(){ return quanlity * plan.summerRate; } function regularCharge(){ return quanlity * plan.regularRate + plan.regularServiceCharge; } charge = isSummer() ? summerCharge() : regularCharge();
-
目的:统一处理条件语句
-
场景:当检查条件各不相同,最终行为一致时
-
例子:
if(age < 18) return 'younger'; if(exprience < 5) return 'younger'; if(!isPassTest) return 'younger';
if(isYounger()) return 'younger'; function isYounger(){ return age<18 || exprience < 5 || !isPassTest }
-
目的:减少逻辑的复杂度
-
场景:当出现需要单独检查某个特定条件时
-
例子:
function getPayment(){ let result; if(isRead){ result = deadAmount(); }else{ if(isSeparated){ result = seperatedAmount(); }else{ if(isRetired){ result = retiredAmount(); }else{ result = normalAmount(); } } } }
function getPayment(){ if(isRead) return deadAmount(); if(isSeparated) return seperatedAmount(); if(isRetired) return retiredAmount(); return normalAmount(); }
-
目的:增强扩展性,减少逻辑的复杂度
-
场景:当多个逻辑处理情况
-
例子:
switch(bird.type){ case 'EuropeanSwallow': return 'EuropeanSwallow'; case 'AfricanSwallow': return 'AfricanSwallow'; default: return 'unknown'; }
class EuropeanSwallow{ get name(){ return 'EuropeanSwallow'; } } class AfricanSwallow{ get name(){ return 'AfricanSwallow'; } }
-
目的:提供复用性以及统一性
-
场景:如果某部分逻辑都在检查某个特殊值,并且处理的逻辑也都相同
-
例子:
if(customer === 'unknown'){ customerName = 'occupant'; }
class UnknowCustomer{ get name(){ return 'occupant'; } }
-
目的:保障传入值是可预测的,预习发现测试的BUG
-
例子:
if(this.discountRate){ base = base - this.discountRate * base; }
asset(this.discountRate>0); if(this.discountRate){ base = base - this.discountRate * base; }
-
目的:减少函数副作用 =》任何有返回值的函数,都要减少它的副作用
-
场景:当函数中又有查询,又有命令
-
例子:
function getTotalOutstadingAndSendBill(person){ const result = customer.invoice.reduce((total, each)=>each.amount + total, 0); sendBill(); return result; }
function getTotalOutstading(person){ return customer.invoice.reduce((total, each)=>each.amount + total, 0); } function sendBill(){} function handler(){ const totalOutstading = getTotalstading(); sendBilld(); }
-
目的:增强函数的功能
-
场景:如果发现多个函数逻辑相似,只有某1-2个字面量不同
-
例子:
function tenPercentRaise(person){ person.salary = person.salary.multiply(1.1); } function fivePercentRaise(person){ person.salary = person.salary.multiply(0.05); }
function raise(person, factor){ person.salary = person.salary.multiply(factor); }
-
目的:代码更清晰,减少函数的复杂度
-
场景:如果参数值影响函数内部的控制流
-
例子:
function setDimension(name, value){ if(name === 'height'){} if(name === 'width'){} }
function setHeight(value){ this._height = value } function setWidth(value){ this._width = value }
-
目的:缩短参数列表,参数配置灵活
-
场景:如果传入多个参数传值
-
例子:
const{low, high} = temperature; if(isValidTemperature(low,high)){};
if(isValidTemperature(temperature)){};
-
对立:以参数取代查询
-
目的:减少传参,从而减少调用者的成本,保持
-
场景:如果传入多个参数,并且从一个参数推导出另一个参数
-
例子:
availableVacation(employee, employee.grade); function availableVacation(employee,grade){}
availableVacation(employee); function availableVacation(employee){ const{grade} = employee; }
-
对立:以查询取代参数
-
目的:减少函数的副作用,以及引用关系,保持函数的纯净度
-
场景:如果函数引用了一个全局变量,或者引用想移除的元素
-
例子:
const weather ={}; targetTemperature(plan) function targetTemperature(plan){ const{curTemperature} = weather; }
const weather ={}; targetTemperature(plan, weather) function targetTemperature(plan, weather){ const{curTemperature} = weather; }
-
目的:防止某字段被修改
-
场景:当不希望某个字段被修改时
-
例子:
class Person{ get id(){} set id(name){} }
class Person{ get id(){} }
-
目的:增加灵活性
-
场景:不存在继承关系时
-
例子:
const leadEngineer = new Employee('name','E');
const leadEngineer = createEngineer('name');
-
对立:以函数取代命令
-
目的:命令对象提供更大的灵活性,并且还可以支持撤销、生命周期的管理等附加操作
-
场景:当普通函数无法提供强有力灵活性
-
例子:
function score(candidate, media){ let result = 0; let healthLevel = 0; } function hasPassMedicalExam(){}
class Scorer{ constructor(candidate, medicalExam){ this._candidate = candidate; this._medicalExam = medicalExam; } execute(){ let result = 0; let healthLevel = 0; } hasPassMedicalExam(){} }
-
对立:以命令取代函数
-
目的:函数简单化
-
场景:大多数情况下,只想调用一个函数,完成自己的工作,不需要函数那么复杂
-
例子:
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; }
继承体系里上下调整:函数上移、字段上移、构造函数本体上移、函数下移、字段下移
继承体系添加新类或者删除旧类:移除子类、提取超类、折叠继承体系
一个字段仅用于类型码使用:以子类取代类型码
如果本来使用集成的场景变得不再适合:以委托取代子类、以委托取代超类
-
对立:函数下移
-
目的:提高复用性,减少重复
-
场景:当函数被大部分部分子类用到时
-
例子:
class Employee{} class Salesman extends Employee{ get name(){} } class Engineer extends Employee{ get name(){} }
class Employee{ get name(){} } class Salesman extends Employee{} class Engineer extends Employee{}
-
目的:提高复用性
-
场景:当多个子类有公共字段
-
例子:
class Party{} class Employee extends Party{ constructor(id){ this._id = id; } } class Salesman extends Employee{ constructor(id){ this._id = id; this._name = name; } }
class Party{ constructor(id){ this._id = id; } } class Employee extends Party{ constructor(id){ super(id); } } class Salesman extends Employee{ constructor(id){ super(id); this._name = name; } }
-
对立:函数上移
-
目的:内聚子类的方法
-
场景:当函数被小部分子类用到时
-
例子:
class Employee{ get quota(){} } class Salesman extends Employee{} class Engineer extends Employee{}
class Employee{} class Salesman extends Employee{ get quota(){} } class Engineer extends Employee{}
-
对立:字段上移
-
目的:内聚子类的字段
-
场景:当字段被小部分子类用到时
-
例子:
class Employee{ constuctor(quote){ this._quote = quote; } } class Salesman extends Employee{ constuctor(quote){ super(quote); } } class Engineer extends Employee{}
class Employee{} class Salesman extends Employee{ constuctor(quote){ this._quote = quote; } } class Engineer extends Employee{}
-
对立:移除子类
-
目的:更明确地表达数据与类型之间的关系,增强子类的扩展性
-
场景:当不同的状态码表现不同的行为时
-
例子:
function createEmployee(name, type){ return new Employee(name, type); }
function createEmployee(name, type){ const employeeTypes = (name) =>{ return{ 'engineer': new Engineer(name), 'salesman': new Salesman(name) } } return employeeTypes(name)[type]; }
-
对立:以子类取代类型码
-
目的:减少系统复杂度
-
场景:当子类的用处太少时,当子类简单
-
例子:
class Person{ get genderCode(){ return 'X'; } } class Male extends Person{ get genderCode(){ return 'M'; } } class Female extends Person { get genderCode(){ return 'F'; } }
class Person{ constructor(genderCode){ this._genderCode = genderCode; } get genderCode(){ return this._genderCode; } }
-
目的:把重复的行为收拢起来
-
场景:当多个子类的方法基本一致时
-
例子:
class Department{ get totalAnnualCost(){} get name(){} } class Employee{ get annualCost(){} get name(){} get id(){} }
class Party{ get totalAnnualCost(){} get name(){} } class Department extends Party{ get primaryCost(){} } class Employee extends Party{ get id(){} }
-
目的:合并子类,减少不必要的子类,降低系统复杂度
-
场景:当超类与子类没有多大差别
-
例子:
class Employee{} class Sales extends Employee{}
class Employee{}
-
目的:增强类的扩展性
-
场景:当子类可能存在多种类型上的变化
-
例子
class Booking{ constuctor(show, date){ this._show = show; this._date = date; } } class PremiumBooking extends Booking{ constructor(show, date, extras){ super(show, date); this._extras = extras; } }
class Booking{ constuctor(show, date){ this._show = show; this._date = date; this._premium = null; } bePremium(extras){ this._premium = new PremiumBookingDelegate(this, extras); } } class PremiumBookingDelegate{ constructor(root, extras){ this._root = root; this._extras = extras; } }
-
目的:增强类的扩展性
-
场景:当超类的部分方法不适用于子类,不清晰的继承关系
-
例子:
class List{} class Stack extends List{}
class List{} class Stack{ constructor(){ this._list = new List(); } }
超类的所有方法都适用于子类,子类的所有实例都是超类的实例 => 使用继承尽量使用继承
如果发现继承有问题(扩展困难),再使用以委托取代超类