策略模式
什么是策略模式?
策略模式是一种行为型设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以互相替换。策略模式使得算法可以独立于使用它的客户端而变化。
核心思想:
将不同的算法(策略)封装成独立的类,这些类实现一个共同的接口。客户端可以根据需要选择并使用不同的策略,而无需修改客户端自身的代码。这使得算法的选择和实现变得灵活和可扩展。
简单来说,就像你有很多种出行方案(策略):
坐公交车 (策略 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),排序有多种算法(冒泡、快排、归并)。
如果一个对象有很多行为,而且这些行为在运行时根据状态而变化,可以使用策略模式将这些行为封装到不同的策略类中。
当一个类定义了多种行为,并且这些行为以多个条件语句的形式出现时。 策略模式可以将这些条件分支移入它们各自的策略类中,以代替这些条件语句。
当你想在不修改客户端代码的情况下,动态地改变对象的行为时。
当你想避免暴露复杂的、与算法相关的数据结构时。
与状态模式的区别:
策略模式和状态模式在结构上非常相似(都有一个环境类、一个抽象接口和多个具体实现类),但它们的意图不同:
策略模式: 关注的是算法的选择和替换,客户端主动选择使用哪个策略。策略之间通常是平行的,可以互相替换。
状态模式: 关注的是对象在不同状态下的行为变化,状态的转换通常是由对象内部条件或者外部事件触发的,客户端通常不直接选择状态。状态之间通常有明确的转换关系。
简单来说,策略模式是“我选择用什么方法做这件事”,而状态模式是“我在什么状态下,就应该怎么做这件事”。