一、核心概念
策略模式(Strategy Pattern)是一种行为型设计模式,它的核心思想是将算法(或行为)封装成独立的策略类,使得它们可以互相替换,从而让算法的变化独立于使用算法的客户端。
解决的问题:
当一个类中包含多种类似的算法(或行为),且这些算法可能频繁变化时,直接在类中用 if-else 或 switch 判断逻辑会导致代码臃肿、难以维护。策略模式通过将每种算法封装成独立的策略类,消除了条件判断,同时让算法的扩展和替换更灵活。策略模式是一种非常实用的设计模式,它允许你在运行时选择算法的行为。
策略模式包含三个主要角色:
- 策略接口(Strategy Interface):定义所有具体策略必须实现的公共方法。
- 具体策略(Concrete Strategies):实现策略接口的具体算法。
- 上下文(Context):持有一个策略接口的引用,并在运行时调用具体策略的方法。
二、 传统实现方式(使用条件语句)
在不使用策略模式的情况下,通常会将所有折扣逻辑集中在一个类中,通过 if-else 或 switch 语句来判断使用哪种折扣算法。
以下是 Java 代码示例:
class Order {
private String memberType; // 会员类型:NORMAL, SILVER, GOLD
private double amount; // 订单金额
public Order(String memberType, double amount) {
this.memberType = memberType;
this.amount = amount;
}
// 计算最终价格(包含所有折扣逻辑)
public double calculateFinalPrice() {
double finalPrice = amount;
// 根据会员类型应用不同折扣
if ("SILVER".equals(memberType)) {
finalPrice = amount * 0.95; // 银卡会员95折
} else if ("GOLD".equals(memberType)) {
finalPrice = amount * 0.9; // 金卡会员9折
}
// 可以继续添加更多会员类型的折扣逻辑...
return finalPrice;
}
}
// 使用示例
public class TraditionalExample {
public static void main(String[] args) {
// 创建普通会员订单
Order normalOrder = new Order("NORMAL", 1000);
System.out.println("普通会员价格: " + normalOrder.calculateFinalPrice());
// 创建银卡会员订单
Order silverOrder = new Order("SILVER", 1000);
System.out.println("银卡会员价格: " + silverOrder.calculateFinalPrice());
// 创建金卡会员订单
Order goldOrder = new Order("GOLD", 1000);
System.out.println("金卡会员价格: " + goldOrder.calculateFinalPrice());
}
}
传统实现的问题:
- 代码臃肿:所有折扣逻辑集中在一个方法中,随着会员类型增加,calculateFinalPrice() 会变得非常庞大。
- 违反开闭原则:新增会员类型(如铂金会员:“PLATINUM”)时,必须修改 Order类 的源代码,增加新的 else if 分支。
- 缺乏复用性:如果其他地方需要使用相同的折扣算法,无法直接复用,只能复制粘贴代码。 难以维护:大量条件语句使代码可读性降低,维护成本增加。
三、Java 实现策略模式
下面是一个使用 Java 实现的策略模式示例,以计算不同会员等级的折扣为例:
1.策略接口
// 策略接口:定义计算折扣的方法
public interface DiscountStrategy {
double applyDiscount(double amount);
}
2.具体策略 (实现类)
// 具体策略:普通会员(无折扣)
public class NormalMemberStrategy implements DiscountStrategy {
@Override
public double applyDiscount(double amount) {
return amount; // 无折扣
}
}
// 具体策略:银卡会员(95折)
public class SilverMemberStrategy implements DiscountStrategy {
@Override
public double applyDiscount(double amount) {
return amount * 0.95; // 95折
}
}
// 具体策略:金卡会员(9折)
public class GoldMemberStrategy implements DiscountStrategy {
@Override
public double applyDiscount(double amount) {
return amount * 0.9; // 9折
}
}
3.上下文
// 上下文:订单类
public class Order {
private DiscountStrategy discountStrategy;
// 通过构造函数注入策略
public Order(DiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
// 允许在运行时更改策略
public void setDiscountStrategy(DiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
// 应用折扣计算最终价格
public double calculateFinalPrice(double amount) {
return discountStrategy.applyDiscount(amount);
}
}
4.使用示例
// 使用示例
public class StrategyPatternExample {
public static void main(String[] args) {
// 创建一个普通会员订单
Order normalOrder = new Order(new NormalMemberStrategy());
System.out.println("普通会员最终价格: " + normalOrder.calculateFinalPrice(1000));
// 创建一个银卡会员订单
Order silverOrder = new Order(new SilverMemberStrategy());
System.out.println("银卡会员最终价格: " + silverOrder.calculateFinalPrice(1000));
// 创建一个金卡会员订单
Order goldOrder = new Order(new GoldMemberStrategy());
System.out.println("金卡会员最终价格: " + goldOrder.calculateFinalPrice(1000));
// 动态更改策略:普通会员升级为银卡会员
normalOrder.setDiscountStrategy(new SilverMemberStrategy());
System.out.println("升级后普通会员最终价格: " + normalOrder.calculateFinalPrice(1000));
}
}
四、Java 8+ 的改进
在 Java 8 及以后,可以使用函数式接口和 Lambda 表达式简化策略模式的实现:针对一些简单逻辑的实现,无需为每个策略创建具体的实现类,直接用 Lambda 表达式表示策略逻辑。
1.策略接口
// 使用函数式接口替代传统策略接口
@FunctionalInterface
public interface DiscountStrategy {
double applyDiscount(double amount);
}
函数式接口是指只包含一个抽象方法的接口(可以包含多个默认方法或静态方法,但抽象方法必须仅有一个)。这个接口可以用于Lambda 表达式或方法引用(函数式接口是 Lambda 表达式的基础)。
抽象策略的抽象方法可以有多个,此例子针对单个抽象方法而言。
2.上下文
// 上下文类保持不变
public class Order {
private DiscountStrategy discountStrategy;
public Order(DiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
public void setDiscountStrategy(DiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
public double calculateFinalPrice(double amount) {
return discountStrategy.applyDiscount(amount);
}
}
3.使用示例
// 使用示例
public class StrategyPatternWithLambda {
public static void main(String[] args) {
// 使用Lambda表达式创建具体策略
DiscountStrategy normalStrategy = amount -> amount; // 无折扣
DiscountStrategy silverStrategy = amount -> amount * 0.95; // 95折
DiscountStrategy goldStrategy = amount -> amount * 0.9; // 9折
// 创建订单并应用不同策略
Order order = new Order(normalStrategy);
System.out.println("普通会员价格: " + order.calculateFinalPrice(1000));
order.setDiscountStrategy(silverStrategy);
System.out.println("银卡会员价格: " + order.calculateFinalPrice(1000));
order.setDiscountStrategy(goldStrategy);
System.out.println("金卡会员价格: " + order.calculateFinalPrice(1000));
}
}
五、 策略模式的优势
- 符合开闭原则:新增策略时无需修改现有代码,只需实现策略接口。
- 避免条件语句:替代复杂的 if-else 或 switch 逻辑。
- 运行时动态切换:可以在运行时根据需要更换策略。
- 代码复用:策略类可以在多个上下文中复用。
对比点 | 传统实现(条件语句) | 策略模式 |
---|---|---|
代码结构 | 折扣逻辑集中在一个类中,通过条件语句分支 | 折扣逻辑分散到独立的策略类中 |
扩展性 | 新增策略需修改原有代码(违反开闭原则) | 新增策略只需添加新类(符合开闭原则) |
复用性 | 逻辑难以复用 | 策略类可在多个地方复用 |
复杂度 | 随着策略增加,代码复杂度呈指数级增长 | 策略类独立,整体复杂度低 |
运行时切换 | 难以在运行时动态切换策略 | 可以通过setStrategy()方法动态切换 |
六、应用场景
- 支付方式选择(信用卡、支付宝、微信支付等)。
- 针对不同厂家的数据对接。
- 排序算法选择(冒泡排序、快速排序、归并排序等)。
- 压缩算法选择(ZIP、GZIP、BZIP2 等)。
- 会员等级折扣计算(如示例所示)。
One More
在策略模式中,具体策略类可通过封装私有方法实现差异化逻辑,其核心优势在于使用者只需依赖抽象策略接口,无需关心具体实现细节。这些私有方法被隔离在各自类中,不会对接口调用产生影响;而传统设计往往将多种策略的实现逻辑混杂在一起,导致代码耦合度高、可维护性差。