重构:改善已有代码的设计

前文导读: 代码设计不仅是为了实现软件功能,更是便于其他开发者阅读。所以一个良好的代码设计及其重要。

提炼函数

如果一段代码需要看很久才能知道其用处,可以将其封装为函数。并以这段代码所做的事情进行函数命名。将只做一件事的多行代码,封装到函数中。

// ==================重构前==================  
function printOwing(invoice) {  
    let outstanding = 0;  
    console.log("***********************");  
    console.log("**** Customer Owes ****");  
    console.log("***********************");  
}  
  
// ==================重构后==================  
function printOwing(invoice) {  
    let outstanding = 0;  
    printBanner()  
}  
  
function printBanner() {  
    console.log("***********************");  
    console.log("**** Customer Owes ****");  
    console.log("***********************");  
}

函数参数化

如果多个函数逻辑相似,只有一些字面量值的差异,可以考虑将函数合并为一个,将差异化的内容转化为参数。

// ==================重构前==================
// 点击异常项  
clickFaultsItem(item){  
    this.$u.route({  
        url:'xxx',  
        params:{  
            id: item.id,  
            type'异常'  
        }  
    })  
}  
  
// 点击正常项  
clickNormalItem(item){  
    this.$u.route({  
        url:'xxx',  
        params:{  
            id: item.id,  
            type'正常'  
        }  
    })  
}  
  
// ==================重构后==================  
clickItem(id, type){  
     this.$u.route({  
        url:'xxx',  
        params:{id, type}  
    })  
}

使用策略模式替换“胖”分支

如果代码中存在大量的if else以及switch case代码。可以使用策略模式将各个分支独立出来。

策略模式就是一个策略名称,会对应这个策略的具体实施。

// 重构前
const getPrice = (type,originPrice)=>{
    if(type==='a'){
        return originPrice/2;
    }else if(type==='b'){
        return oringinPrice-10;
    }else if(type === 'c'){
        return originPrice*2;
    }
}

//重构后
const price = {
    a(originPrice){
        return originPrice/2;
    },
    b(originPrice){
        return oringinPrice-10;
    },
    c(originPrice){
        return originPrice*2;
    }
}
const getPrice=(type,originPrice)=>{
    return price[type](originPrice);
}
const a =getPrice('a',100);

提炼变量

如果一个表达式非常复杂,则可以考虑采用局部变量来代替。

内联变量

当变量本身没有并没有表达式更容易理解时,可以直接使用表达式,而非变量。

封装变量

封装变量是指将变量封装起来,只能通过函数进行访问。即getter和setter的思想

// ==================重构前==================  
let defaultOwner = {firstName"Martin"lastName"Fowler"};  
// 访问  
spaceship.owner = defaultOwner;  
// 赋值  
defaultOwner = {firstName"Rebecca"lastName"Parsons"};  
  
// ==================重构后==================  
function getDefaultOwner() {return defaultOwner;}  
function setDefaultOwner(arg) {defaultOwner = arg;}  
// 访问  
spaceship.owner = getDefaultOwner();  
// 赋值  
setDefaultOwner({firstName"Rebecca"lastName"Parsons"});

拆分阶段

当分析出一段代码在同时干两件事的时候,可以把他们拆分成按照顺序执行的函数。 比如后续代码的执行需要依赖于前序代码计算的值,则可以将前序逻辑封装到同一的函数中,将得到的值作为参数传递给后续代码进行处理。

如此处理可以让代码的层次更加清晰,不要总是把代码揉合到一起。

拆分循环 拆分变量

当一个循环或者变量需要身兼数职,那么可以将其拆分开。即所谓的单一原则,尽量让一个一段代码或者一个变量只承担一件事情。

let temp = 2*(height+width);
console.log(temp);
temp = height*width;
console.log(temp);

上面的temp变量既要承担计算周长的任务,又要承担计算面积的任务。我们其实可以将计算周长、面积分别用两个变量来实现。

分解条件表达式

如果一个函数中条件表达式对应的内容非常多,则可以将每个分支的内容拆分成对应的函数。 好处是:

  • 提高可读性
  • 可以突出条件逻辑

合并条件表达式

在阅读代码的过程,如果发现不同的条件表达式中,最终的结果是相同的。可以考虑将条件通过逻辑或或者与链接起来。

以卫语句取代嵌套条件表达式

什么是卫语句?

如果某个条件极其罕见,就应该单独检查该条件,并在该条件为真时立刻从函数中返回。这样的单独检查常常被称为“卫语句”(guard clauses)。

在if-else判断语句中,通常两个分支语句是同等重要的。我们需要将不同的条件罗列出来。但是卫语句所表达的语义是,这种情况并不是函数真正关心的,如果真的走到这步了,做些简单的逻辑,然后退出即可。

// ==================重构前==================  
function payAmount(employee) {  
    let result;  
    if(employee.isSeparated) {  
        result = {amount0reasonCode:"SEP"};  
    }  
    else {  
        if (employee.isRetired) {  
            result = {amount0reasonCode"RET"};  
        }  
        else {  
            result = someFinalComputation();  
        }  
    }  
    return result;  
}  
  
// ==================重构后==================  
function payAmount(employee) {  
    if (employee.isSeparatedreturn {amount0reasonCode"SEP"};  
    if (employee.isRetiredreturn {amount0reasonCode"RET"};  
    return someFinalComputation();  
}

将查询函数和修改函数分离

如果一个函数在查询的同时进行了修改操作。此时,可以先完成查询,将查询出来的内容传递到修改函数中。而不是把不同的功能全部揉合到一起。

// ==================重构前==================  
function alertForMiscreant (people) {  
    for (const p of people) {  
        if (p === "Don") {  
            setOffAlarms();  
            return "Don";  
        }  
        if (p === "John") {  
            setOffAlarms();  
            return "John";}  
    }  
    return "";  
}  
// 调用方  
const found = alertForMiscreant(people);  
  
// ==================重构后==================  
function findMiscreant (people) {  
    for (const p of people) {  
        if (p === "Don") {  
            return "Don";  
        }  
        if (p === "John") {  
            return "John";  
        }  
    }  
    return "";  
}  
function alertForMiscreant (people) {  
    if (findMiscreant(people) !== ""setOffAlarms();  
}  
// 调用方  
const found = findMiscreant(people);  
alertForMiscreant(people);

注:这篇文章是在学习mp.weixin.qq.com/s/UUCR42jFz… 的过程中的学习笔记。深有感触,学习完明白了代码在健壮的同时,也要区分职责,尽量让函数去做单一的事情,而不是将所有的内容揉合在一起。