字节面试题:什么是策略模式,实现上有什么特点,相比if-else的好处是什么

67 阅读6分钟

策略模式真香!我彻底告别了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时,不妨停下来想一想:「这里是否可以用策略模式来重构?」

记住,我们写的代码不仅要让机器能懂,更要让人能懂。毕竟,下个维护这段代码的人,可能就是你自己!😉


「思考题:」 在你的项目中,有哪些地方可以用策略模式来优化呢?欢迎在评论区分享你的想法!👇