设计模式之策略模式(Strategy)
策略模式(Strategy Pattern)是行为型设计模式之一,它定义了一系列的算法,并将每一个算法封装起来,使它们可以互换。策略模式让算法独立于使用它的客户而变化,是将算法从使用它的类中独立出来,从而使得客户可以根据需要选择不同的算法来完成任务。
1. 策略模式概述
1.1 定义
策略模式是一种定义一系列算法的设计模式,它将每个算法封装成一个类,并使它们可以相互替换。策略模式让算法的变化独立于使用算法的客户。策略模式不仅仅是封装算法,还使得不同的算法可以互相替换,且不会影响到客户端的代码。
简单来说,策略模式的核心思想是:定义一系列算法,并将它们封装到独立的策略类中,客户端可以根据需要选择使用某一个算法。
1.2 组成
策略模式通常由以下几个角色组成:
- Context(上下文):维护一个对某个具体策略对象的引用,最终将请求委托给具体策略对象来完成。
- Strategy(策略接口):通常是一个接口,定义一系列的算法。不同的具体策略类实现该接口并提供具体的算法。
- ConcreteStrategy(具体策略类):实现了策略接口的具体算法。
1.3 优点与缺点
优点
- 算法独立于使用它的客户:客户端可以通过选择不同的策略来改变行为。
- 避免了大量的条件语句:通过策略类来避免在客户端代码中写大量的条件判断语句。
- 易于扩展:通过添加新的策略类,可以轻松扩展算法集,而不会影响现有代码。
- 符合开闭原则:现有代码不需要修改,新增算法时只需增加新的策略类即可。
缺点
- 客户端必须知道所有的策略:客户端需要了解不同的策略和如何使用它们,这可能会增加一定的复杂性。
- 策略类数目可能较多:如果算法种类很多,可能会导致策略类的数量增多,从而增加系统的复杂度。
2. 策略模式的使用场景
策略模式适用于以下场景:
- 有多种算法的选择:当一个类在其内部有多个算法,并且这些算法有相似的功能时,可以通过策略模式来封装它们,并且根据不同情况选择不同的算法。
- 算法需要独立于使用它的客户端而变化:当多个类之间共享同一个算法,而这个算法在使用时可能需要变化时,可以通过策略模式实现算法的动态切换。
- 避免条件判断:如果系统中包含很多类似的条件判断,并且每个判断对应不同的行为,可以通过策略模式将这些行为封装成不同的策略类,从而避免复杂的条件判断。
3. 策略模式的结构与实现
3.1 示例:电商平台的运费计算
在电商平台中,通常根据不同的订单条件(如:运送方式、订单金额、用户所在地等)来计算运费。这里可以使用策略模式来处理运费计算的不同策略。
3.1.1 代码实现
我们通过这个电商平台的例子来说明策略模式的实现过程。
1. 定义策略接口
javaCopy Code// 运费计算策略接口
public interface ShippingStrategy {
double calculateShippingCost(Order order);
}
2. 实现具体的策略类
javaCopy Code// 快递运送策略
public class ExpressShippingStrategy implements ShippingStrategy {
@Override
public double calculateShippingCost(Order order) {
return order.getWeight() * 10.0;
}
}
// 标准运送策略
public class StandardShippingStrategy implements ShippingStrategy {
@Override
public double calculateShippingCost(Order order) {
return order.getWeight() * 5.0;
}
}
// 免费运送策略
public class FreeShippingStrategy implements ShippingStrategy {
@Override
public double calculateShippingCost(Order order) {
return 0.0; // 免费运送
}
}
3. 上下文类(Order)
javaCopy Code// 订单类
public class Order {
private double weight; // 订单重量
private ShippingStrategy shippingStrategy; // 运送策略
public Order(double weight) {
this.weight = weight;
}
public void setShippingStrategy(ShippingStrategy shippingStrategy) {
this.shippingStrategy = shippingStrategy;
}
public double getWeight() {
return weight;
}
public double calculateShippingCost() {
return shippingStrategy.calculateShippingCost(this);
}
}
4. 客户端代码
javaCopy Codepublic class StrategyPatternDemo {
public static void main(String[] args) {
// 创建订单对象
Order order = new Order(5.0); // 重量为5kg的订单
// 设置使用快递运送策略
order.setShippingStrategy(new ExpressShippingStrategy());
System.out.println("运费(快递): " + order.calculateShippingCost());
// 设置使用标准运送策略
order.setShippingStrategy(new StandardShippingStrategy());
System.out.println("运费(标准): " + order.calculateShippingCost());
// 设置使用免费运送策略
order.setShippingStrategy(new FreeShippingStrategy());
System.out.println("运费(免费): " + order.calculateShippingCost());
}
}
3.1.2 运行结果
Copy Code运费(快递): 50.0
运费(标准): 25.0
运费(免费): 0.0
3.1.3 解析
在这个例子中,我们通过不同的运送策略(如:快递、标准、免费)来计算运费。通过策略模式,我们可以将不同的运送方式封装到不同的策略类中,客户端可以动态选择合适的策略来计算运费,避免了条件判断语句的使用,同时使得算法(运送方式)可以独立于客户端代码进行扩展和变化。
4. 真实世界中的策略模式应用场景
4.1 支付方式选择
在电商平台中,用户可以选择不同的支付方式,如:支付宝、微信支付、银行卡支付等。每种支付方式的支付流程和费用计算方式不同。使用策略模式,系统可以根据用户的选择来切换不同的支付策略。
4.2 排序算法
在数据排序的场景中,可能会根据不同的需求使用不同的排序算法,比如快速排序、冒泡排序、插入排序等。通过策略模式,可以将这些排序算法封装成独立的策略类,客户端可以根据需求选择不同的排序算法。
4.3 旅游推荐系统
在旅游推荐系统中,可能会根据用户的兴趣、预算、天气等因素推荐不同的旅游路线。每个推荐策略可能会使用不同的规则和算法来生成推荐列表。通过策略模式,可以将不同的推荐算法封装成策略类,系统根据用户的选择来动态选择不同的推荐策略。
5. 策略模式与其他设计模式的关系
策略模式与其他一些设计模式有一定的关联和区别:
- 策略模式与工厂模式:工厂模式和策略模式的结合常常被用来创建策略对象。在某些情况下,工厂模式可以用来生成策略对象,而策略模式则用来选择和使用这些策略。
- 策略模式与状态模式:状态模式与策略模式非常相似,二者都允许动态改变对象的行为。但它们的应用场景不同,策略模式用于算法的选择,而状态模式用于对象状态的变化。
6. 总结
策略模式通过将不同的算法封装成独立的策略类,使得它们可以互换。客户端可以根据需要选择不同的算法,避免了大量条件判断语句的使用。策略模式具有很高的灵活性和扩展性,尤其适用于算法集较为丰富且可能会变化的场景。
在实际开发中,策略模式常常与工厂模式结合使用,通过工厂模式动态创建策略对象,并使用策略模式实现不同算法的选择。它不仅符合开闭原则,还能有效提高系统的可维护性和可扩展性。