策略模式
什么是策略模式?
策略模式是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以互相替换。策略模式使得算法可以独立于使用它的客户端而变化。
核心思想:
将不同的算法(策略)封装成独立的类,这些类实现一个共同的接口。客户端可以根据需要选择并使用不同的策略,而无需修改客户端自身的代码。这使得算法的选择和实现变得灵活和可扩展。
简单来说,就像你有很多种出行方案(策略):
-
坐公交车 (策略 A)
-
骑自行车 (策略 B)
-
打出租车 (策略 C)
你可以根据具体情况(如天气、距离、时间)选择一种最合适的出行方案,而选择方案的行为和具体方案的执行是分开的。
主要角色:
-
环境类 (Context):
-
持有一个对策略对象的引用。
-
它不直接执行算法,而是将请求委托给它所持有的策略对象。
-
客户端通常通过环境类来调用具体的策略。
-
可以提供一个方法来设置或切换当前的策略。
-
-
抽象策略 (Strategy):
-
定义所有支持的算法的公共接口或抽象类。
-
环境类通过这个接口调用具体策略类中定义的算法。
-
-
具体策略 (Concrete Strategy):
-
实现了抽象策略接口或继承了抽象策略类的具体类。
-
封装了具体的算法或行为。
-
每个具体策略类代表一种特定的算法实现。
-
工作流程:
-
客户端创建一个具体策略对象。
-
客户端创建一个环境类对象,并将具体策略对象设置到环境类中。
-
当客户端需要执行某个操作时,它调用环境类的方法。
-
环境类将请求委托给它所持有的具体策略对象去执行。
-
具体策略对象执行其封装的算法,并返回结果(如果需要)。
代码示例 (Java):
假设我们要实现一个购物车结算功能,可以有不同的促销策略,如打折、满减。
// 1. 抽象策略 (PromotionStrategy)
interface PromotionStrategy {
double applyDiscount(double originalPrice);
}
// 2. 具体策略 (DiscountStrategy, FullReductionStrategy)
class DiscountStrategy implements PromotionStrategy {
private double discountRate; // 例如 0.8 表示八折
public DiscountStrategy(double discountRate) {
this.discountRate = discountRate;
}
@Override
public double applyDiscount(double originalPrice) {
System.out.println("Applying discount strategy: " + (discountRate * 100) + "% off");
return originalPrice * discountRate;
}
}
class FullReductionStrategy implements PromotionStrategy {
private double conditionAmount; // 满多少
private double reductionAmount; // 减多少
public FullReductionStrategy(double conditionAmount, double reductionAmount) {
this.conditionAmount = conditionAmount;
this.reductionAmount = reductionAmount;
}
@Override
public double applyDiscount(double originalPrice) {
System.out.println("Applying full reduction strategy: spend " + conditionAmount + ", get " + reductionAmount + " off");
if (originalPrice >= conditionAmount) {
return originalPrice - reductionAmount;
}
return originalPrice;
}
}
class NoPromotionStrategy implements PromotionStrategy {
@Override
public double applyDiscount(double originalPrice) {
System.out.println("No promotion applied.");
return originalPrice;
}
}
// 3. 环境类 (ShoppingCart)
class ShoppingCart {
private PromotionStrategy promotionStrategy;
private double originalPrice;
public ShoppingCart(double originalPrice) {
this.originalPrice = originalPrice;
// 默认无促销
this.promotionStrategy = new NoPromotionStrategy();
}
// 设置促销策略
public void setPromotionStrategy(PromotionStrategy promotionStrategy) {
this.promotionStrategy = promotionStrategy;
}
public double checkout() {
return promotionStrategy.applyDiscount(originalPrice);
}
}
// 客户端使用
public class Client {
public static void main(String[] args) {
// 场景1: 商品原价 100元,无促销
ShoppingCart cart1 = new ShoppingCart(100.0);
System.out.println("Cart 1 Final Price: " + cart1.checkout()); // 输出: No promotion applied. Cart 1 Final Price: 100.0
System.out.println("--------------------");
// 场景2: 商品原价 200元,使用八折促销
ShoppingCart cart2 = new ShoppingCart(200.0);
cart2.setPromotionStrategy(new DiscountStrategy(0.8));
System.out.println("Cart 2 Final Price: " + cart2.checkout()); // 输出: Applying discount strategy: 80.0% off. Cart 2 Final Price: 160.0
System.out.println("--------------------");
// 场景3: 商品原价 300元,使用满250减50促销
ShoppingCart cart3 = new ShoppingCart(300.0);
cart3.setPromotionStrategy(new FullReductionStrategy(250, 50));
System.out.println("Cart 3 Final Price: " + cart3.checkout()); // 输出: Applying full reduction strategy: spend 250.0, get 50.0 off. Cart 3 Final Price: 250.0
System.out.println("--------------------");
// 场景4: 商品原价 150元,使用满250减50促销 (不满足条件)
ShoppingCart cart4 = new ShoppingCart(150.0);
cart4.setPromotionStrategy(new FullReductionStrategy(250, 50));
System.out.println("Cart 4 Final Price: " + cart4.checkout()); // 输出: Applying full reduction strategy: spend 250.0, get 50.0 off. Cart 4 Final Price: 150.0
}
}
优点:
-
算法可以自由切换: 客户端可以根据需要动态地选择和切换不同的策略。
-
避免使用多重条件转移语句(if-else 或 switch-case): 将不同的行为封装到不同的策略类中,使得代码更清晰,避免了冗长的条件判断。
-
扩展性好: 增加新的策略非常容易,只需要添加一个新的具体策略类实现抽象策略接口即可,符合开闭原则。
-
策略类可以复用: 每个策略都是一个独立的对象,可以在不同的环境类中复用。
-
将算法的实现细节与客户端代码分离: 客户端只需要知道如何使用策略,而不需要关心策略的具体实现。
缺点:
-
客户端必须知道所有的策略类: 客户端需要了解有哪些可用的策略,并自己选择使用哪一个。这在一定程度上增加了客户端的复杂性。(可以通过结合工厂模式来隐藏具体策略类)。
-
会产生很多策略类: 如果策略很多,会导致类的数量增加。
适用场景:
-
一个系统需要在许多算法中选择一种时。 例如,文件压缩有多种算法(ZIP, RAR, GZIP),排序有多种算法(冒泡、快排、归并)。
-
如果一个对象有很多行为,而且这些行为在运行时根据状态而变化,可以使用策略模式将这些行为封装到不同的策略类中。
-
当一个类定义了多种行为,并且这些行为以多个条件语句的形式出现时。 策略模式可以将这些条件分支移入它们各自的策略类中,以代替这些条件语句。
-
当你想在不修改客户端代码的情况下,动态地改变对象的行为时。
-
当你想避免暴露复杂的、与算法相关的数据结构时。
与状态模式的区别:
策略模式和状态模式在结构上非常相似(都有一个环境类、一个抽象接口和多个具体实现类),但它们的意图不同:
-
策略模式: 关注的是算法的选择和替换,客户端主动选择使用哪个策略。策略之间通常是平行的,可以互相替换。
-
状态模式: 关注的是对象在不同状态下的行为变化,状态的转换通常是由对象内部条件或者外部事件触发的,客户端通常不直接选择状态。状态之间通常有明确的转换关系。
简单来说,策略模式是"我选择用什么方法做这件事",而状态模式是"我在什么状态下,就应该怎么做这件事"。