22、重构与设计模式(上):策略模式、工厂模式在重构中的应用
设计模式这东西,很多人觉得是“高大上”的东西。其实不然。我做了十几年项目,越来越觉得设计模式就是重构的“目的地”。你想想看,代码写烂了,要改好,改着改着就发现——咦,这不就是某个模式吗?
今天咱们聊两个最常用的:策略模式和工厂模式。这两个模式在重构中出现的频率,我个人觉得能排进前三。
策略模式:把“如果-否则”拆成零件
先看一段代码。这种代码我见过无数次了:
public double calculatePrice(String userType, double amount) {
if ("VIP".equals(userType)) {
return amount * 0.8;
} else if ("GOLD".equals(userType)) {
return amount * 0.85;
} else if ("SILVER".equals(userType)) {
return amount * 0.9;
} else {
return amount;
}
}
这段代码有什么问题?
- 新增一个用户等级,就得改这个方法
- 多个地方用到类似逻辑,就得复制粘贴
- 测试起来很麻烦,要测所有分支
说白了,就是变化点没有隔离。每种计价方式都耦合在一起了。
核心思路:把每种计价方式封装成独立的策略类,让它们可以互相替换。
重构后的代码长这样:
// 策略接口
public interface DiscountStrategy {
double calculate(double amount);
}
// 具体策略
public class VipDiscount implements DiscountStrategy {
public double calculate(double amount) {
return amount * 0.8;
}
}
public class GoldDiscount implements DiscountStrategy {
public double calculate(double amount) {
return amount * 0.85;
}
}
public class SilverDiscount implements DiscountStrategy {
public double calculate(double amount) {
return amount * 0.9;
}
}
// 上下文类
public class PriceCalculator {
private DiscountStrategy strategy;
public PriceCalculator(DiscountStrategy strategy) {
this.strategy = strategy;
}
public double calculate(double amount) {
return strategy.calculate(amount);
}
}
嗯,这里要注意:策略模式不是让你把所有if-else都干掉。它解决的是“行为有多种变体,且变体可能频繁变化”的问题。
我的经验:如果if-else只有两三个分支,而且几乎不变,就别硬套策略模式。过度设计比没有设计更可怕。
工厂模式:把“创建对象”这件事收口
工厂模式在重构中出现的频率,说实话比策略模式还高。为什么?因为对象的创建往往是耦合的源头。
我曾经接手过一个项目,里面new对象的地方散落得到处都是。后来要换一个实现类,好家伙,改了二十多个文件。这就是典型的“创建逻辑分散”问题。
看个例子:
// 重构前:到处new
public class OrderService {
public void processOrder(String type) {
Payment payment;
if ("ALIPAY".equals(type)) {
payment = new Alipay();
} else if ("WECHAT".equals(type)) {
payment = new WechatPay();
} else {
payment = new CreditCard();
}
payment.pay();
}
}
// 重构后:用简单工厂收口
public class PaymentFactory {
public static Payment create(String type) {
if ("ALIPAY".equals(type)) {
return new Alipay();
} else if ("WECHAT".equals(type)) {
return new WechatPay();
} else {
return new CreditCard();
}
}
}
public class OrderService {
public void processOrder(String type) {
Payment payment = PaymentFactory.create(type);
payment.pay();
}
}
你看,改动不大,但效果很明显:
- 创建逻辑集中到一处
- 新增支付方式只改工厂类
- 调用方不需要知道具体实现
注意:简单工厂不是真正的“工厂模式”,它只是一个封装手法。真正的工厂模式有工厂方法模式和抽象工厂模式,咱们后面会细讲。
策略+工厂:黄金搭档
在实际项目中,策略模式和工厂模式经常一起出现。策略模式负责“行为”,工厂模式负责“创建”。
我画了一张图,帮你理解它们的关系:
这张图想表达什么?说白了就是:客户端不关心具体策略怎么来的,也不关心策略怎么实现的。它只跟策略接口打交道。
工厂负责把“创建”这件事收口。策略负责把“行为”这件事解耦。两者配合,代码的扩展性会好很多。
重构中的实战套路
我总结了一个简单的套路,你在重构时可以照着做:
- 找变化点:看看代码里哪些地方经常改,或者多个地方有类似的if-else
- 提取接口:把变化的行为抽象成接口
- 封装策略:每个分支变成一个策略类
- 引入工厂:把策略的创建集中到工厂里
- 替换调用:把原来的if-else替换成工厂+策略的组合
小技巧:不要一次性重构所有地方。先挑一个最乱的地方下手,改好了再扩散。我曾经一口气改了十几个文件,结果测试全崩了。后来学乖了,一次只改一个点。
什么时候不该用
说了这么多好处,也得说说坑。这两种模式不是银弹:
| 场景 | 建议 |
|---|---|
| 只有两三个分支,且几乎不变 | 别用,if-else更直接 |
| 策略类之间差异极小 | 考虑用参数化,别硬拆类 |
| 团队不熟悉设计模式 | 先简单封装,别引入太多概念 |
| 性能要求极高 | 策略模式有额外对象开销,注意 |
我曾经在一个性能敏感的项目里用了策略模式,结果每个请求都要new一堆策略对象,GC压力很大。后来改成了策略实例复用,才解决问题。嗯,这就是经验教训。
总结一下
策略模式和工厂模式,是重构中最常用的两个模式。它们解决的核心问题就两个:
- 策略模式:把“怎么做”拆开,让行为可以替换
- 工厂模式:把“怎么创建”收口,让创建逻辑集中
两者配合使用,代码的扩展性和可维护性会提升一个档次。但记住,模式是工具,不是目的。别为了用模式而用模式。
下一节咱们继续聊其他模式在重构中的应用,到时候再分享一些我在项目中踩过的坑。
公众号:蓝海资料掘金营,微信deep3321