“面条式代码”(英文叫 spaghetti code)是指那种结构混乱、逻辑纠缠、可读性差、难以维护和扩展的代码。就像一盘乱七八糟的面条一样,一头拉动牵扯一大片,让人很难理清头绪。
常见特征包括:
-
各种变量乱用,全局变量横飞
-
函数特别长,一个函数几百上千行
-
代码里充满 if/else、switch、goto(如果是老代码的话)
-
没有清晰的模块划分或职责分离
-
改动一个地方容易牵连一大堆地方,极容易出 bug
比如这种:
function processOrder(order) {
if (order.type === 'normal') {
if (order.amount > 100) {
if (order.customer.isVIP) {
// 做一些处理
} else {
// 做另一种处理
}
} else {
// 又是一种处理
}
} else if (order.type === 'special') {
if (order.isGift) {
// gift 特别处理
} else {
if (order.delivery === 'express') {
// express 特别处理
} else {
// 普通 special 处理
}
}
}
// ...后面还有一堆
}
一开始还能跟着逻辑走,过一阵子代码多了,维护的人一看到就头大。
一般来说,面条式代码的出现是因为:
-
项目一开始没设计清楚,边写边改
-
开发时间赶工,临时 patch 层层叠加
-
多人维护,各自为战,没有统一风格和规范
-
缺乏重构意识
怎么避免面条式代码?
-
多用函数/方法抽离,保持每个函数短小清晰
-
按业务模块合理拆分
-
合理使用设计模式,比如策略模式、状态模式
-
定期重构,控制代码复杂度
-
保持一致的编码规范和风格
举个例子。假设我们有一个计算订单折扣的函数,它的逻辑很复杂,代码像面条一样纠结。以下是初版的“面条式”代码:
面条式代码
function calculateDiscount(order) {
let discount = 0;
if (order.type === 'normal') {
if (order.amount > 100) {
if (order.customer.isVIP) {
discount = 0.2;
} else {
discount = 0.1;
}
} else {
if (order.customer.isVIP) {
discount = 0.15;
} else {
discount = 0.05;
}
}
} else if (order.type === 'special') {
if (order.isGift) {
discount = 0.3;
} else {
if (order.amount > 200) {
discount = 0.25;
} else {
if (order.customer.isVIP) {
discount = 0.2;
} else {
discount = 0.1;
}
}
}
}
return discount;
}
重构后的代码
我们将这段代码拆分成了几个小的函数,清晰、易于扩展,也方便单元测试:
function getNormalDiscount(order) {
if (order.amount > 100) {
return order.customer.isVIP ? 0.2 : 0.1;
} else {
return order.customer.isVIP ? 0.15 : 0.05;
}
}
function getSpecialDiscount(order) {
if (order.isGift) {
return 0.3;
} else {
return order.amount > 200 ? 0.25 : (order.customer.isVIP ? 0.2 : 0.1);
}
}
function calculateDiscount(order) {
if (order.type === 'normal') {
return getNormalDiscount(order);
} else if (order.type === 'special') {
return getSpecialDiscount(order);
}
return 0;
}
重构后的好处:
-
清晰分工:我们为每种订单类型提取了专门的函数。每个函数职责单一,代码更清晰,易于理解。
-
便于扩展:如果以后需要添加新的折扣类型,只需要添加一个新的函数,
calculateDiscount
函数也只需要加一行判断即可。 -
容易维护:修改某种类型的折扣时,只需要修改对应的函数,不会影响其他部分。
-
可测试性:每个小函数可以单独测试,减少了因一个大函数改动而带来的 bug 风险。
看,代码变得更简洁了,对吧?这样维护起来也不容易出错,问题也好追踪。如果你有类似的代码,尝试重构看看,效果会很明显哦!