优惠券代码优化思考
代码优化,总感觉缺点味道,所有我把这次优化,说成了重构,事实上,这次优化,确实相当于重构了
什么是重构,为什么要重构
- 重构是在不改变软件可观测行为的前提下,调整
代码结构,提高软件的可理解性,降低变更成本 - 重构是一种经济使用行为,而非道德使然,如果它不能更快更好的开发,那么他是毫无意义的
- 代码的写法应该使人理解他所需的时间最小化,进而变更代码所需要的时间也会最小化
重构的原则
- 重构的目标:提高迭代效率 (经济驱动而不是道德驱动)
- 每一次提交代码,都应该使代码变得更好,先重构,再开发
- 增量式重构 = 自动化测试+持续集成+TDD驱动重构(待补充)
- 每一次重构完成都应该提交代码,这样就可以在下一次重构出现问题的时候,迅速回退到上一次正常工作时的状态,这一点很有用!“
- 这样的重构还有个好处,那就是可以保证代码随时都是可发布的状态,因为并没有影响到整体功能的运行。”
代码的坏味道
神秘的命名( Mysterious Name)
- 猜谜时间会越来越多
重复代码 (Repeat Code)
过长参数列表(Long Parameter List)
(() => {
function priceRange(products, min, max, isOutSide) {
if (isOutSide) {
return products.filter((r) => r.price < min || r.price > max);
} else {
return products.filter((r) => r.price > min && r.price < max);
}
}
const products = [
{ name: 'apple', price: 6 },
{ name: 'banana', price: 7 },
{ name: 'orange', price: 15 },
{ name: 'cookie', price: 0.5 },
];
const range = { min: 5, max: 8 };
const insidePriceProducts = priceRange(products, range.min, range.max, false);
console.log('insidePriceProducts', insidePriceProducts);
})();
(() => {
// 把 priceRange 的 isOutSide 标记参数移除了,让人疑惑的标记参数就被移除了,取而代之的是两个语义更加清晰的函数
function priceOutSideRange(products, min, max) {
return products.filter((r) => r.price < min || r.price > max);
}
function priceInsideRange(products, min, max) {
return products.filter((r) => r.price > min && r.price < max);
}
const products = [
{ name: 'apple', price: 6 },
{ name: 'banana', price: 7 },
{ name: 'orange', price: 15 },
{ name: 'cookie', price: 0.5 },
];
const range = { min: 5, max: 8 };
const insidePriceProducts = priceInsideRange(products, range.min, range.max);
console.log('insidePriceProducts', insidePriceProducts);
})();
(() => {
// range 范围的判定还是需要花费一定时间理解,而 range 作为我们刚识别出来的一种结构,可以继续进行重构,就像这样。”
class Range {
constructor(min, max) {
this.min = min;
this.max = max;
}
outside(num) {
return num < this.min || num > this.max;
}
inside(num) {
return num > this.min && num < this.max;
}
}
function priceInsideRange(products, range) {
return products.filter((r) => range.inside(r.price));
}
const products = [
{ name: 'apple', price: 6 },
{ name: 'banana', price: 7 },
{ name: 'orange', price: 15 },
{ name: 'cookie', price: 0.5 },
];
const insidePriceProducts = priceInsideRange(products, new Range(5, 8));
console.log('insidePriceProducts', insidePriceProducts);
})();
过长函数(Long Function)
全局数据(Global Data)
// global.js
// ...
let userAuthInfo = {
platform: 'pc',
token: '',
};
export { userAuthInfo };
// main.js
userAuthInfo.token = localStorage.token;
// request.js
const reply1 = await login();
userAuthInfo.token = reply1.data.token;
// business.js
await request({ authInfo: userAuthInfo });
// 但是我现在可以在代码库的任何一个角落都可以修改 platform 和 token,而且没有任何机制可以探测出到底哪段代码做出了修改,这就是全局数据的问题
// 每当我们看到可能被各处的代码污染的数据,我们还是需要全局数据用一个函数包装起来,至少你就能看见修改它的地方,并开始控制对它的访问,这里我做个简单的封装
// +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
let userAuthInfo = {
platform: 'pc',
token: '',
};
function getUserAuthInfo() {
return { ...userAuthInfo };
}
function setToken(token) {
userAuthInfo.token = token;
}
export { getUserAuthInfo, setToken };
// main.js
setToken(localStorage.token);
// request.js
const reply = await login();
setToken(reply.data.token);
// business.js
await request({ authInfo: getUserAuthInfo() });
// 这样一来,通过对象引用就无法修改源对象了
优惠券优化
- 神秘命名
- 代码臃肿(组件化)
- 细节思考,超前设计(样式居中,样式居中,内容过长的处理)
未使用
已使用 已过期
不可用
可用
空
空
命名规范
- list 和detail
样式兼容性考虑
- 内容多了的处理考虑