策略模式(Strategy Pattern)是一种行为型设计模式,它定义了一系列算法,并将每个算法封装到独立的策略类中,使得它们可以相互替换。策略模式使得算法可以独立于使用它的客户端而变化。这种模式的核心思想是通过使用组合而非继承来实现算法的灵活替换和扩展。
1. 策略模式的定义
**策略模式允许定义一系列可互换的算法或行为,并通过策略类的组合,动态地选择使用哪一个算法。**这种模式通常用于系统中有多种方法可以执行某一任务的场景,且希望可以在运行时决定采用哪一种方法。
2. 策略模式的结构
策略模式的典型结构包含以下几个角色:
- 策略接口(Strategy):这是一个抽象的策略接口,定义了所有策略类必须实现的行为。
- 具体策略类(ConcreteStrategy):每个具体策略类都实现了策略接口,表示某种具体的算法或行为。
- 上下文类(Context):上下文类维护一个对策略对象的引用,客户端可以通过上下文类来调用不同的策略。上下文类并不关心策略的具体实现,只负责调用策略接口中定义的方法。
3. 策略模式的实现示例
假设我们有一个场景,需要对商品进行不同的打折策略。可以使用策略模式来实现不同的折扣算法,如打9折、满100减10元、打8折等。
// 策略接口
interface DiscountStrategy {
double applyDiscount(double price);
}
// 具体策略类:打9折
class PercentageDiscountStrategy implements DiscountStrategy {
private double discountPercentage;
public PercentageDiscountStrategy(double discountPercentage) {
this.discountPercentage = discountPercentage;
}
@Override
public double applyDiscount(double price) {
return price * (1 - discountPercentage / 100);
}
}
// 具体策略类:满100减10元
class ThresholdDiscountStrategy implements DiscountStrategy {
@Override
public double applyDiscount(double price) {
if (price >= 100) {
return price - 10;
}
return price;
}
}
// 具体策略类:打8折
class FlatDiscountStrategy implements DiscountStrategy {
@Override
public double applyDiscount(double price) {
return price * 0.8;
}
}
// 上下文类
class ShoppingCart {
private DiscountStrategy discountStrategy;
public ShoppingCart(DiscountStrategy discountStrategy) {
this.discountStrategy = discountStrategy;
}
public double calculatePrice(double price) {
return discountStrategy.applyDiscount(price);
}
}
// 客户端代码
public class StrategyPatternExample {
public static void main(String[] args) {
// 使用不同的折扣策略
ShoppingCart cart1 = new ShoppingCart(new PercentageDiscountStrategy(10)); // 打9折
System.out.println("Price after 10% discount: " + cart1.calculatePrice(120));
ShoppingCart cart2 = new ShoppingCart(new ThresholdDiscountStrategy()); // 满100减10元
System.out.println("Price after threshold discount: " + cart2.calculatePrice(120));
ShoppingCart cart3 = new ShoppingCart(new FlatDiscountStrategy()); // 打8折
System.out.println("Price after flat 20% discount: " + cart3.calculatePrice(120));
}
}
代码说明:
- DiscountStrategy 是一个策略接口,定义了 applyDiscount() 方法,所有策略类都需要实现这个方法。
- PercentageDiscountStrategy、ThresholdDiscountStrategy 和 FlatDiscountStrategy 是具体策略类,它们各自实现了不同的折扣算法。
- ShoppingCart 是上下文类,维护了一个 DiscountStrategy 的引用,通过组合不同的策略类来实现动态的折扣算法。
- 客户端可以在运行时根据需求选择使用哪一种策略类,并通过 ShoppingCart 来应用对应的折扣策略。
4. 策略模式的优缺点
优点:
- 算法的灵活切换:通过策略模式,可以在运行时根据具体需求动态切换不同的算法或行为,客户端无需关心策略的内部实现。
- 遵循开闭原则:增加新的策略时,只需要添加新的策略类,不需要修改已有的上下文类或策略接口代码,保持了代码的可扩展性。
- 避免条件语句:在传统的实现中,可能会使用大量的 if-else 或 switch-case 来选择不同的算法,而策略模式通过将算法封装到独立的策略类中,消除了条件分支语句,使得代码结构更清晰。
缺点:
- 策略类增多:随着策略的增多,策略类的数量也会随之增加,可能导致类数量膨胀,增加了系统的复杂性。
- 客户端需要了解不同策略的差异:客户端需要知道有哪些策略类以及这些策略的不同之处,才能正确地选择合适的策略。这对客户端代码有一定的了解要求。
5. 策略模式的使用场景
策略模式适用于以下场景:
- 多种算法可互换的场景:如果某个任务有多种不同的算法或实现方式,可以使用策略模式将不同算法封装为不同的策略类,并通过上下文类动态地选择具体的算法执行。
- 消除条件判断语句:当某个系统中充斥着大量的条件判断语句(如 if-else 或 switch-case),通过策略模式可以将这些条件判断转化为策略类,从而简化代码结构。
- 希望隐藏算法细节:如果希望客户端不必关心算法的具体实现细节,只需根据需求选择算法,就可以使用策略模式。
6. 策略模式与其他设计模式的比较
- 策略模式 vs 状态模式:策略模式和状态模式的结构非常相似,但它们的意图不同。策略模式旨在将算法进行封装,并在运行时选择使用哪个算法;而状态模式的核心是通过上下文对象的状态变化来改变对象的行为。
- 策略模式 vs 工厂模式:策略模式是通过不同的策略类来封装不同的算法,而工厂模式是通过工厂类来负责对象的创建。策略模式通常用来替代条件判断语句,而工厂模式更多用于对象的创建。
7. 策略模式的改进
在策略模式的实现中,上下文类通过组合方式使用策略类,有时可以进一步改进,减少客户端对策略的选择。可以结合工厂模式,让工厂来决定选择哪个策略类。还可以结合反射和配置文件,使得策略类的选择更加灵活。
class StrategyFactory {
public static DiscountStrategy getStrategy(String strategyType) {
if (strategyType.equals("PERCENTAGE")) {
return new PercentageDiscountStrategy(10);
} else if (strategyType.equals("THRESHOLD")) {
return new ThresholdDiscountStrategy();
} else if (strategyType.equals("FLAT")) {
return new FlatDiscountStrategy();
} else {
throw new IllegalArgumentException("Unknown strategy type.");
}
}
}
通过这种方式,客户端只需要传入策略类型,工厂类会负责返回相应的策略对象,进一步降低客户端对策略类的依赖。
总结
策略模式通过将不同的算法封装为独立的策略类,并通过上下文类在运行时动态选择具体的策略,提供了一种灵活扩展和复用的设计方法。它消除了大量的条件判断语句,并使得算法可以轻松扩展而无需修改现有代码。策略模式适用于需要灵活切换算法、避免复杂条件分支的场景,是一种非常实用的设计模式。