前文导读: 代码设计不仅是为了实现软件功能,更是便于其他开发者阅读。所以一个良好的代码设计及其重要。
提炼函数
如果一段代码需要看很久才能知道其用处,可以将其封装为函数。并以这段代码所做的事情进行函数命名。将只做一件事的多行代码,封装到函数中。
// ==================重构前==================
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 = {amount: 0, reasonCode:"SEP"};
}
else {
if (employee.isRetired) {
result = {amount: 0, reasonCode: "RET"};
}
else {
result = someFinalComputation();
}
}
return result;
}
// ==================重构后==================
function payAmount(employee) {
if (employee.isSeparated) return {amount: 0, reasonCode: "SEP"};
if (employee.isRetired) return {amount: 0, reasonCode: "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… 的过程中的学习笔记。深有感触,学习完明白了代码在健壮的同时,也要区分职责,尽量让函数去做单一的事情,而不是将所有的内容揉合在一起。