策略模式
模式的意图
在软件开发中,经常会遇到需要根据不同的条件来实现不同行为的场景。这种场景下,策略模式(Strategy Pattern)就是一种非常有用的设计模式。
策略模式属于行为型模式,允许我们定义一系列算法,并将其封装在独立的策略类中,使得它们可以互相替换。通过使用策略模式,我们能够灵活地选择和切换不同的算法,而无需修改原有的代码,替代⼤量 if else 的逻辑。
动机
- 当存在多种实现方式,且需要在运行时动态选择具体实现时,策略模式非常有用。例如,一个购物应用可能需要根据用户的会员等级来计算折扣,不同等级对应不同的计算方式,这时就可以使用策略模式来实现。
- 当存在一组类似的行为,只是实现细节略有不同,但又不希望通过继承来添加新的子类时,策略模式也很适用。它将这组行为封装在独立的策略类中,并通过委托的方式在上下文对象中使用。
- 例如 支付方式选择:一个电子商务平台可以根据用户的选择来使用不同的支付策略,例如信用卡支付、支付宝支付、微信支付等。
类结构
在策略模式中,有三个核心角色:上下文(Context)、策略接口(Strategy)和具体策略类(Concrete Strategy)。
- 上下文(Context):封装了具体策略的执行逻辑,提供给客户端使用的接口。上下文通常包含一个指向策略接口的引用,用于调用具体策略的方法。
- 策略接口(Strategy):定义了一组算法或行为的公共接口,所有具体策略都必须实现该接口。
- 具体策略类(Concrete Strategy):实现了策略接口,提供了具体的算法或行为。
代码示例
接下来以支付方式选择为例,展示代码
-
上下文类
1 2 3 4 5 6 7 8 9 10 11 12
// 上下文类 public class PaymentContext { private PaymentStrategy paymentStrategy; public PaymentContext(PaymentStrategy paymentStrategy) { this.paymentStrategy = paymentStrategy; } public void pay(double amount) { paymentStrategy.pay(amount); } }
-
策略接口
1 2 3 4
//策略接口 public interface PaymentStrategy { void pay(double amount); }
-
策略接口实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14
//具体策略类 public class CreditCardPayment implements PaymentStrategy { public void pay(double amount) { System.out.println("使用信用卡支付:" + amount); // 具体的支付逻辑 } } public class WeChatPay implements PaymentStrategy { public void pay(double amount) { System.out.println("使用微信支付:" + amount); // 具体的支付逻辑 } }
-
调用
1 2 3 4 5 6 7 8 9 10 11 12
// 使用示例 public class Main { public static void main(String[] args) { PaymentStrategy strategy = new CreditCardPayment(); PaymentContext context = new PaymentContext(strategy); context.pay(100.0); strategy = new WeChatPay(); context = new PaymentContext(strategy); context.pay(200.0); } }
-
输出
1 2
使用信用卡支付:100.0 使用微信支付:200.0
-
策略模式的优缺点
策略模式的优点包括:
- 松耦合:策略模式将不同的策略封装在独立的类中,与上下文对象解耦,增加了代码的灵活性和可维护性。
- 易于扩展:可以通过添加新的策略类来扩展系统的功能,无需修改现有代码。
- 符合开闭原则:对于新的策略,无需修改上下文对象,只需要实现新的策略接口即可。
策略模式的缺点包括:
- 类数量增多:每个具体策略都需要一个独立的类,如果策略较多,将导致类的数量增加。
- 上层必须知道所有策略类:上层模块必须知道有哪些策略,并选择合适的策略进行使用,这与迪米特法则是相违背的。
注意事项: 如果一个系统的策略多于四个,就需要考虑使用混合模式,解决策略类膨胀的问题,否则日后的系统维护就会成为一个烫手山芋。
策略模式的优化
使用Map取消 Context 类
我们可以将策略实现类放进 Map 中,根据 key 去选择具体的策略,就不必事先定义 Context 类。
|
|
策略枚举解决策略类膨胀
策略枚举可以解决策略类过多的问题。
我们对原装的策略模式进行改造,把原有定义在抽象策略中的方法移植到枚举中,让枚举成员成为一个具体策略。
|
|
在上面的代码中,我们定义了一个枚举类型 PaymentStrategy
,其中包含两个枚举常量 CREDIT_CARD
和 WECHAT_PAY
。每个枚举常量都重写了 pay()
方法,用于具体的支付逻辑
|
|
注意:策略枚举是一个非常优秀和方便的模式,但是它受枚举类型的限制,每个枚举项都是 public、final、static 的,扩展性受到了一定的约束,因此在系统开发中,策略枚举一般担当不经常发生变化的角色。
SpringBoot中的策略模式
|
|
使用的时候 @Autowired
或者 @Resource
即可,SpringBoot会帮我们把实现类自动注入注入Map。
|
|
总结
策略模式是一种强大而灵活的设计模式,它可以帮助我们处理不同的算法或行为,并使系统更具可维护性和扩展性。通过封装具体的策略类和使用上下文对象,我们可以轻松地选择和切换不同的策略,而无需修改现有的代码。