在代码评审会上,我经常看到这样的场景:一个方法里嵌套了七八层if/else,就像俄罗斯套娃一样让人眼花缭乱。这种代码不仅难以维护,更会在后续迭代中成为"定时炸弹"。上周我接手的一个老项目就遇到了这个问题——因为业务逻辑变更,需要修改一个核心判断条件,结果发现这个条件分散在二十多个if分支里,每个分支还有自己的子条件,改起来简直是一场噩梦。
if/else本身并没有错,它是编程语言中最基础的控制结构之一。问题出在我们使用它的方式上。当业务逻辑变得复杂时,很多开发者会不自觉地添加一个又一个的条件分支,最终形成所谓的"箭头代码"(代码缩进形成箭头形状)。这种代码至少有三大罪状:可读性差、难以扩展、容易引入bug。
策略模式是我最常用的优化手段,特别适合处理根据不同类型执行不同逻辑的场景。比如电商系统中的折扣计算:
java复制// 优化前
if(user.isVIP()) {
price = price * 0.8;
} else if(user.isNew()) {
price = price * 0.9;
} else if(campaign.isActive()) {
price = price * 0.95;
} else {
price = price;
}
优化后的版本:
java复制// 定义策略接口
interface DiscountStrategy {
double apply(double price);
}
// 实现具体策略
class VIPDiscount implements DiscountStrategy {
public double apply(double price) {
return price * 0.8;
}
}
// 策略上下文
class DiscountContext {
private DiscountStrategy strategy;
public void setStrategy(DiscountStrategy strategy) {
this.strategy = strategy;
}
public double execute(double price) {
return strategy.apply(price);
}
}
实际项目中,我通常会结合工厂模式来创建策略对象。注意策略对象应该是无状态的,这样才能安全地复用。
上周我重构了一个订单状态管理系统,原来的代码是这样的:
python复制if order.status == "pending":
if payment_received:
order.status = "paid"
elif timeout:
order.status = "cancelled"
elif order.status == "paid":
if shipment_sent:
order.status = "shipped"
elif refund_requested:
order.status = "refunding"
# 还有更多elif...
用状态模式重构后:
python复制class OrderState(ABC):
@abstractmethod
def handle(self, order, event):
pass
class PendingState(OrderState):
def handle(self, order, event):
if event == "payment_received":
order.state = PaidState()
elif event == "timeout":
order.state = CancelledState()
class Order:
def __init__(self):
self.state = PendingState()
def process_event(self, event):
self.state.handle(self, event)
状态模式特别适合业务规则经常变更的场景。去年我们电商平台的退货政策改了三次,但因为用了状态模式,每次改动都只需要修改对应的状态类,不会影响其他业务逻辑。
处理审批流程时,我见过这样的代码:
javascript复制function handleRequest(request) {
if (request.type === 'leave' && request.days <= 3) {
return manager.approve(request);
} else if (request.type === 'leave' && request.days <= 10) {
return director.approve(request);
} else if (request.type === 'purchase' && request.amount <= 5000) {
return finance.approve(request);
}
// 更多条件...
}
用责任链模式重构:
javascript复制class Approver {
constructor() {
this.next = null;
}
setNext(approver) {
this.next = approver;
}
approve(request) {
if (this.canHandle(request)) {
return this.doApprove(request);
} else if (this.next) {
return this.next.approve(request);
}
throw new Error('No handler found');
}
abstract canHandle(request);
abstract doApprove(request);
}
class Manager extends Approver {
canHandle(request) {
return request.type === 'leave' && request.days <= 3;
}
// 实现doApprove...
}
这个模式最大的好处是可以动态调整处理链。去年疫情期间,我们临时增加了防疫物资的特殊审批流程,只需要在链中插入一个新的处理节点,完全不用修改现有代码。
对于简单的条件映射,我强烈推荐表驱动法。比如处理错误码转换:
csharp复制// 优化前
string GetErrorMessage(int code) {
if (code == 404) return "Not Found";
else if (code == 500) return "Server Error";
else if (code == 403) return "Forbidden";
// 更多else if...
}
优化后:
csharp复制private static readonly Dictionary<int, string> ErrorMessages = new Dictionary<int, string>
{
{400, "Bad Request"},
{401, "Unauthorized"},
{403, "Forbidden"},
// 更多映射...
};
string GetErrorMessage(int code) {
if (ErrorMessages.TryGetValue(code, out var message)) {
return message;
}
return "Unknown Error";
}
在最近的一个物联网项目中,我们用表驱动法处理了200多种设备状态码,代码量减少了70%,而且新增状态码只需要在字典中添加一行,完全符合开闭原则。
面对复杂的条件逻辑时,我通常这样决策:
去年设计支付系统时,我们同时用到了这四种模式:
虽然设计模式能让代码更清晰,但也要注意性能影响。我有几点经验:
在实时交易系统中,我们对策略模式做了特殊优化 - 提前缓存策略对象,避免重复创建。而在一个后台批处理系统中,我们甚至允许责任链有10个节点,因为批处理的吞吐量比延迟更重要。
过度设计:曾经为了用模式而用模式,把简单的3个if分支改成了策略模式,反而增加了复杂度。现在我遵循"三次原则" - 只有当一个逻辑被复制粘贴三次以上,才考虑引入设计模式。
状态不一致:有次用状态模式时,忘记处理某些边缘状态转换,导致系统出现诡异bug。现在我一定会画完整的状态转换图,并用单元测试覆盖所有转换路径。
链式调用中断:早期用责任链模式时,忘记设置next节点,导致请求丢失。现在我会在单元测试中验证整个调用链的完整性。
在团队协作中,我建立了这样的规范:
这些规范让我们的代码库保持了良好的可维护性,即使项目规模扩大了三倍,新成员也能快速上手。