策略模式真香!我彻底告别了if-else的噩梦
❝
看到满屏的if-else,我差点把键盘砸了……
❞
作为一名程序员,你是否曾经面对过这样的代码?
if (userType.equals("VIP")) {
// 处理VIP逻辑
} else if (userType.equals("ORDINARY")) {
// 处理普通用户逻辑
} else if (userType.equals("NEW")) {
// 处理新用户逻辑
} else if (userType.equals("OLD")) {
// 处理老用户逻辑
} else if (userType.equals("SILVER")) {
// 处理银卡用户逻辑
} else if (userType.equals("GOLD")) {
// 处理金卡用户逻辑
}
// ... 还有无数个else if在等着你
看到这样的代码,你是不是也血压升高?🤯 我曾经维护过一个支付系统,里面光是支付方式就有十几种,if-else嵌套了**「几十层」**!每次添加新的支付方式,都像是在走钢丝,生怕把原来的逻辑搞崩了。
直到我遇到了**「策略模式」**,才发现原来代码可以写得如此优雅!
什么是策略模式?🕵️♂️
简单来说,策略模式就是:「定义一系列算法,将每个算法封装起来,并且使它们可以互相替换」。
说人话就是:「把不同的处理逻辑放到不同的类中,然后用一个统一的方式来调用」。 它的核心思想是:
❝
✅ 把变化的逻辑(行为)抽离成独立的策略类; ✅ 让调用方在运行时选择使用哪种策略; ✅ 消灭 if-else 链,让扩展更优雅。
❞
一句话总结就是:
❝
「策略模式是“用多态取代 if-else”的艺术。」 🎨
❞
是不是听起来有点抽象?别急,我们来看个实际场景。
实战场景:电商订单折扣
假设我们正在开发一个电商平台,需要根据用户的不同类型计算折扣。传统的if-else写法是这样的:
// 传统的if-else写法 - 不推荐!
public BigDecimal calculateDiscount(String userType, BigDecimal price) {
if ("VIP".equals(userType)) {
return price.multiply(new BigDecimal("0.7")); // VIP打7折
} else if ("NEW".equals(userType)) {
return price.multiply(new BigDecimal("0.9")); // 新用户打9折
} else if ("ORDINARY".equals(userType)) {
return price.multiply(new BigDecimal("0.95")); // 普通用户打95折
}
// 如果再来几种用户类型,这里会越来越长...
return price;
}
这种写法有什么问题呢?
❌ 「难以维护」:每次新增用户类型都要修改这个类 ❌ 「容易出错」:改动一个分支可能影响其他分支 ❌ 「违反开闭原则」:对扩展开放,对修改关闭?不存在的!
策略模式来拯救!🚀
🧱 用图看懂策略模式(思维导图风格)
┌─────────────┐
│ Context │ → 调用策略
└──────┬──────┘
│
▼
┌──────────────┐
│ Strategy接口 │ ← 定义行为规范
└──────┬───────┘
│
┌─────┴─────┐
│ │
▼ ▼
具体策略A 具体策略B
(如VIP) (如NEW)
现在让我们用策略模式重构上面的代码:
Java版本实现
首先,定义一个折扣策略接口:
public interface DiscountStrategy {
// 计算折扣
BigDecimal calculateDiscount(BigDecimal price);
// 返回用户类型
String getUserType();
}
然后,为每种用户类型实现具体的策略类:
// VIP折扣策略
public class VipDiscountStrategy implements DiscountStrategy {
@Override
public BigDecimal calculateDiscount(BigDecimal price) {
return price.multiply(new BigDecimal("0.7"));
}
@Override
public String getUserType() {
return "VIP";
}
}
// 新用户折扣策略
public class NewUserDiscountStrategy implements DiscountStrategy {
@Override
public BigDecimal calculateDiscount(BigDecimal price) {
return price.multiply(new BigDecimal("0.9"));
}
@Override
public String getUserType() {
return "NEW";
}
}
// 普通用户折扣策略
public class OrdinaryDiscountStrategy implements DiscountStrategy {
@Override
public BigDecimal calculateDiscount(BigDecimal price) {
return price.multiply(new BigDecimal("0.95"));
}
@Override
public String getUserType() {
return "ORDINARY";
}
}
最后,创建一个策略上下文来管理这些策略:
public class DiscountContext {
private Map<String, DiscountStrategy> strategyMap = new HashMap<>();
// 注册策略
public void registerStrategy(DiscountStrategy strategy) {
strategyMap.put(strategy.getUserType(), strategy);
}
// 执行折扣计算
public BigDecimal calculateDiscount(String userType, BigDecimal price) {
DiscountStrategy strategy = strategyMap.get(userType);
if (strategy != null) {
return strategy.calculateDiscount(price);
}
return price; // 默认原价
}
}
使用方式:
public class Main {
public static void main(String[] args) {
DiscountContext context = new DiscountContext();
// 注册各种策略
context.registerStrategy(new VipDiscountStrategy());
context.registerStrategy(new NewUserDiscountStrategy());
context.registerStrategy(new OrdinaryDiscountStrategy());
// 使用策略
BigDecimal price = new BigDecimal("100");
System.out.println("VIP价格:" + context.calculateDiscount("VIP", price));
System.out.println("新用户价格:" + context.calculateDiscount("NEW", price));
}
}
Golang版本实现
对于Go语言爱好者,这里也提供Go版本的实现:
// 定义策略接口
type DiscountStrategy interface {
CalculateDiscount(price float64) float64
GetUserType() string
}
// VIP策略
type VipStrategy struct{}
func (v *VipStrategy) CalculateDiscount(price float64) float64 {
return price * 0.7
}
func (v *VipStrategy) GetUserType() string {
return "VIP"
}
// 新用户策略
type NewUserStrategy struct{}
func (n *NewUserStrategy) CalculateDiscount(price float64) float64 {
return price * 0.9
}
func (n *NewUserStrategy) GetUserType() string {
return "NEW"
}
// 策略上下文
type DiscountContext struct {
strategies map[string]DiscountStrategy
}
func NewDiscountContext() *DiscountContext {
return &DiscountContext{
strategies: make(map[string]DiscountStrategy),
}
}
func (d *DiscountContext) RegisterStrategy(strategy DiscountStrategy) {
d.strategies[strategy.GetUserType()] = strategy
}
func (d *DiscountContext) CalculateDiscount(userType string, price float64) float64 {
if strategy, exists := d.strategies[userType]; exists {
return strategy.CalculateDiscount(price)
}
return price
}
使用方式:
func main() {
context := NewDiscountContext()
context.RegisterStrategy(&VipStrategy{})
context.RegisterStrategy(&NewUserStrategy{})
price := 100.0
fmt.Printf("VIP价格: %.2f\n", context.CalculateDiscount("VIP", price))
fmt.Printf("新用户价格: %.2f\n", context.CalculateDiscount("NEW", price))
}
策略模式的优势 ✨
看到这里,你可能已经感受到策略模式的威力了。让我们总结一下它的**「核心优势」**:
✅ 「1. 符合开闭原则」
新增一种用户类型?只需要新增一个策略类,「完全不需要修改现有代码」!
// 新增银卡用户策略 - 只需要新增,不需要修改!
public class SilverDiscountStrategy implements DiscountStrategy {
@Override
public BigDecimal calculateDiscount(BigDecimal price) {
return price.multiply(new BigDecimal("0.8"));
}
@Override
public String getUserType() {
return "SILVER";
}
}
✅ 「2. 代码可读性大幅提升」
每个策略类只关注自己的业务逻辑,职责单一,易于理解和维护。
✅ 「3. 便于单元测试」
可以针对每个策略单独测试,测试用例更加清晰:
@Test
public void testVipDiscount() {
VipDiscountStrategy strategy = new VipDiscountStrategy();
BigDecimal result = strategy.calculateDiscount(new BigDecimal("100"));
assertEquals(new BigDecimal("70"), result);
}
✅ 「4. 动态切换算法」
运行时可以根据不同情况切换不同的策略,非常灵活。
💭 对比 if-else,策略模式赢在哪?
| 对比点 | if-else 方案 | 策略模式 |
|---|---|---|
| 扩展性 | ❌ 每新增一个分支就要改老代码 | ✅ 新增策略类即可 |
| 可维护性 | ❌ 容易出错、难测试 | ✅ 每个策略独立测试 |
| 代码结构 | ❌ 逻辑杂糅,缺乏封装 | ✅ 职责单一,清晰解耦 |
| 运行时灵活性 | ❌ 固定逻辑 | ✅ 可以动态切换策略 |
一句话金句 💬:
❝
“策略模式不是让代码更花哨,而是让变化变得可控。”
❞
实际应用场景 🎯
策略模式在真实项目中应用广泛:
- 「支付系统」:支付宝、微信支付、银行卡等不同支付方式
- 「数据导出」:Excel、PDF、CSV等不同格式导出
- 「消息推送」:短信、邮件、APP推送等不同推送方式
- 「排序算法」:根据不同场景选择快速排序、归并排序等
什么时候不该用策略模式?🤔
当然,策略模式不是银弹。在以下情况下,你可能不需要它:
- 如果策略很少,而且基本不会变化,if-else可能更简单
- 如果策略之间逻辑差异很大,可能需要其他模式
- 过度设计也是要避免的,简单才是王道
总结 💡
「金句来了:」 好的代码不是没有if-else,而是把if-else放在了该放的地方!
策略模式通过**「抽象、封装、组合」**的方式,让我们的代码:
🎯 「更清晰」 - 每个策略只做一件事 🎯 「更灵活」 - 可以动态扩展和替换 🎯 「更健壮」 - 修改一个策略不会影响其他策略
下次当你看到满屏的if-else时,不妨停下来想一想:「这里是否可以用策略模式来重构?」
记住,我们写的代码不仅要让机器能懂,更要让人能懂。毕竟,下个维护这段代码的人,可能就是你自己!😉
「思考题:」 在你的项目中,有哪些地方可以用策略模式来优化呢?欢迎在评论区分享你的想法!👇