通过封装,可以隐藏不需要对外暴露的信息,防止被修改
1、封装记录
参考“第一组重构”中的封装变量
2、封装集合
- 先通过封装变量,封装集合
- 在类上添加“添加集合元素”和 “ 移除集合元素”的函数
class Person {
constructor(name) {
this._name = name
this._course = []
}
get name() { return this._name }
get course() { return this._course }
// 有了单独的添加和移除元素的方法,set 设置函数就没有存在的必要了
// set course(aList) { this._course = aList}
addCourse(aCourse) {
this._course.push(aCourse)
}
removeCourse(aCourse, fnIfAbsent() => {throw new RangeError();}) {
const index = this._aCourse.indexOf(aCourse)
if(index === -1) fnIfAbsent()
else this._course.splice(index, 1)
}
}
3、以查询取代临时变量
// bad
class Order {
constructor(quantity, item) {
this._quantity = quantity
this._item = item
}
get price() {
// 可消灭的临时变量
var basePrice = this._quantity * this._item.price
// 可消灭的临时变量
var discountFactor = 0.98
if(basePrice > 1000) discountFactor -= 0.03
return basePrice * discountFactor
}
}
// good
class Order {
constructor(quantity, item) {
this._quantity = quantity
this._item = item
}
get price() {
return this.basePrice * this.discountFactor
}
get basePrice() {
return this._quantity * this._item.price
}
get discountFactor() {
let result = 0.98
if(this.basePrice > 1000) result -= 0.03
return result
}
}
4、替换算法
// bad
function foundPerson(person) {
for(let i = 0; i < person.length; i++) {
if(person[i] === 'Don') {
return 'Don'
}
if(person[i] === 'John') {
return 'John'
}
if(person[i] === 'Mary') {
return 'Mary'
}
}
}
// good
function foundPerson(person){
const candidates = ['Don', 'John', 'Mary']
return person.find(p => candidates.includes(p)) || ''
}
5、以函数调用取代内联代码
// bad
let appliesToMass = false
for(const s of status) {
if(s === 'MA') appliesToMass = true
}
// good
let appliesToMass = status.includes('MA')
6、拆分循环
如果一次循环中做了多个事情,如果需要修改循环时,都需要重新理解这两件事情。
拆分循环,让一个循环只做一个事情,能让循环更容易理解以及更容易使用。
// bad
let youngest = people[0] ? people[0].age : Infinity
let totalSalary = 0
for(const p of people) {
if(p.age > youngest) youngest = p.age
totalSalary += p.salary
}
return `youngestaAge: ${youngest}, totalSalary: ${totalSalary}`
// good
function youngestaAge() {
let result = people[0] ? people[0].age : Infinity
for(const p of people) {
if(p.age > result) result = p.age
}
return result
}
function totalSalary() {
let result = 0
for(const p of people) {
result += p.salary
}
return result
}
return `youngestaAge: ${youngestaAge()}, totalSalary: ${totalSalary()}`
// better
function youngestaAge() {
return Math.min(...people.map(p => p.age))
}
function totalSalary() {
return people.reduce((total, p) => total + p.salary, 0)
}
return `youngestaAge: ${youngestaAge()}, totalSalary: ${totalSalary()}`
7、以管道取代循环
// 数据源
office, country, telephone
Chigaco, USA, +1 312 373 1000
Beijing, China, +86 400 800 4200
Bangalore, India, +91 80 373 1000
// bad 循环体过于复杂
// 筛选出印度的所有办公室,并返回办公室所在的城市和电话
function acquireData(input) {
const lines = input.split('/n')
let firstLine = true
const result = []
for(const line of lines) {
// 略第一行数据
if(firstLine) {
firstLine = false
continue
}
// 过滤空字符串
if(line.trim() === '') continue
// 将数据转换为数组
const record = line.split(',')
// 过滤,只获取"India"相关数据
if(record[1].trim() === 'India') {
// 映射成需要的数据格式
result.push({city: record[0].trim(), phone: record[2].trim()})
}
}
return result
}
// good
function acquireData(input) {
const lines = input.split('/n')
return lines
.slice(1) // 忽略第一行数据
.filter(line => line.trim() === '') // 过滤空字符串
.map(line => line.split(',')) // 将数据转换为数组
.filter(record => record[1].trim() === 'India') // 过滤,只获取"India"相关数据
.map(record => {{city: record[0].trim(), phone: record[2].trim()}})
}
8、移除死代码
//bad 应该移除的死代码
if(false) {
doSomething()
}
9、拆分变量
变量有变量的用途,如果变量承担多个责任,它应该被分解为多个变量
// bad
let temp = 2 * (height + width)
temp = height + width
// good
const perimeter = 2 * (height + width)
const area = height + width
// bad
function discount(inputValue, quantity) {
if(inputValue > 50) inputValue = inputValue - 2
if(quantity > 100) inputValue = inputValue - 1
return inputValue
}
// good
function discount(inputValue, quantity) {
let result = inputValue
if(inputValue > 50) result = result - 2
if(quantity > 100) result = result - 1
return result
}
10、以查询取代派生变量
// bad -- 数据重复, 每次调用applyAdjustment 都对_production累加一次
class ProductionPlan {
construction(production) {
this._production = production
}
get production() { return this._production}
applyAdjustment(anAdjustment) {
this._adjustments.push(anAdjustment)
this._production += anAdjustment.amount
}
}
// good
class ProductionPlan {
construction(production) {
this._production = production
}
get production() {
return this._adjustments
.reduce((sum, a) => sum + a.mount, 0)
}
applyAdjustment(anAdjustment) {
this._adjustments.push(anAdjustment)
}
}