今天给大家分享的是策略模式,具体内容大纲如下:
生活案例
在这互联网时代,尤其是在城市中,有一帮骑着电瓶车,穿梭在大街小巷中,这帮人就是外卖小哥。
对于点外卖,我也点过不少。有一次,外卖下单的时候,我突然联想到了一个设计模式---策略模式。
策略模式是个啥?
策略模式:英文为Strategy Pattern,是指定义了算法家族、分别封装起来,让他们之间可以相互替换,此设计模式让算法的变化不会影响到使用算法的用户。
英文
Define a family of algorithms,encapsulate each one,and make them interchangeable.
大致意思:定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。
在策略模式中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。
策略模式通用代码
java代码实现如下:
- class Client {
- //抽象策略类 Strategy
- interface IStrategy {
- void algorithm();
- }
- //具体策略类 ConcreteStrategy
- static class ConcreteStrategyA implements IStrategy {
- @Override
- public void algorithm() {
- System.out.println("Strategy A");
- }
- }
- //具体策略类 ConcreteStrategy
- static class ConcreteStrategyB implements IStrategy {
- @Override
- public void algorithm() {
- System.out.println("Strategy B");
- }
- }
- //上下文环境
- static class Context {
- private IStrategy mStrategy;
- public Context(IStrategy strategy) {
- this.mStrategy = strategy;
- }
- public void algorithm() {
- this.mStrategy.algorithm();
- }
- }
- public static void main(String[] args) {
- //选择一个具体策略
- IStrategy strategy = new ConcreteStrategyA();
- //来一个上下文环境
- Context context = new Context(strategy);
- //客户端直接让上下文环境执行算法
- context.algorithm();
- }
- }
从上面的通用代码,我们可以得知其UML图。
策略模式UML图
策略模式中的角色
从 UML 类图中,我们可以看到,策略模式主要包含三种角色:
- 上下文角色(Context):用来操作策略的上下文环境,屏蔽高层模块(客户端)对策略,算法的直接访问,封装可能存在的变化;
- 抽象策略角色(Strategy):规定策略或算法的行为;
- 具体策略角色(ConcreteStrategy):具体的策略或算法实现;
策略模式优缺点
优点
- 策略模式符合开闭原则
- 避免使用多重转换语句,比如:if...else、switch语句。
- 使用策略模式可以提高算法的保密性和安全性
缺点
- 客户端必须知道所有策略,并且自行决定使用哪一种策略
- 代码中产生非常多的策略类,增加后期维护难度
策略模式使用场景
在日常开发中,策略模式适用于以下三种场景:
- 针对同一类型问题,有多重处理方式,每一种都能独立解决问题。
- 算法需要自由切换的场景。
- 需要屏蔽算法规则的场景
这个说起来,还是不太好理解。
下面,我们就来使用生活案例来实现,让大家知道策略模式到底是怎么使用的。
支付案例代码重构,三个版本
外面下单,选择支付方式的时候,我觉这个功能,我们可以模仿着使用策略模式来实现一下。下面我们通过三个版本的迭代来实现,很有意思的。
第一版
先定义一个抽象类Pay:
- //定义抽象类,我们可以把一些共用功能放在抽象类里实现
- //比如:可用余额和本次支付金额进行比较,统一返回“支付失败”
- public abstract class Pay {
- abstract void doPay();
- }
下面模拟三种支付方式:
- public class AliPay extends Pay {
- @Override
- public void doPay() {
- System.out.println("使用支付宝支付");
- }
- }
- public class UnionPay extends Pay {
- @Override
- public void doPay() {
- System.out.println("使用银联支付");
- }
- }
- public class WechatPay extends Pay {
- @Override
- public void doPay() {
- System.out.println("使用微信支付");
- }
- }
我们再来进行支付:
- public class PayTest {
- public static void main(String[] args) {
- //把选择权交给了用户
- Order order = new Order(new WechatPay());
- order.pay();*
- }
- }
运行结果:
- 使用微信支付
这样我们就使用策略模式实现了简单版本的支付,但是其中有个很不爽的地方,就是每次还得自己手工new一个支付方式的对象。鉴于此,我们对第一版进行重构。
第二版
前面的实现都不变,变化的是发起支付的时候,只要前端传一个key过来就可以了,实现如下:
- public class PayTest {
- public static void main(String[] args) {
- String payKey = "Wechat";
- Order order = null;
- //通过判断前端传过来的key,判断使用哪种支付方式
- if (payKey.equals("Ali")) {
- order = new Order(new AliPay());
- } else if (payKey.equals("Wechat")) {
- order = new Order(new WechatPay());
- } else if (payKey.equals("union")) {
- order = new Order(new UnionPay());
- }else {
- //给出一个默认方式
- order = new Order(new WechatPay());
- }
- order.pay();
- }
- }
运行结果
- 使用微信支付
这样我们就实现了,通过前端传过来的key,然后选出对应的支付方式。但是问题又来了,如果支付方式不断增多,那这里的if...else岂不是会越来越多吗?后续维护成本不是越来越大吗?
于是,第三版就有了。
第三版
在第二版中,会出现大量的if...else,会给后续的代码维护带来不便,于是在这一版中,我们对其进行重构,引入了注册式单例模式。
- import java.util.HashMap;
- import java.util.Map;
- public enum PayStrategyEnum {
- ALI_PAY("Ali"),
- WECHAT_PAY("Wechat"),
- UNION_PAY("union"),
- //默认使用微信支付
- DEFAULT_PAY("Wechat");
- private String key;
- PayStrategyEnum(String key) {
- this.key = key;
- }
- private static final Map<String, Pay> payKeyMap = new HashMap();
- static {
- payKeyMap.put(ALI_PAY.key, new AliPay());
- payKeyMap.put(WECHAT_PAY.key, new WechatPay());
- payKeyMap.put(UNION_PAY.key, new UnionPay());
- payKeyMap.put(DEFAULT_PAY.key, new WechatPay());
- }
- public static Pay getPay(String payKey) {
- if (!payKeyMap.containsKey(payKey)) {
- return payKeyMap.get(DEFAULT_PAY.key);
- }
- return payKeyMap.get(payKey);
- }
- }
然后,在订单支付的时候就变成了这样了:
- public class PayTest {
- public static void main(String[] args) {
- String payKey = "Wechat";
- Order order = new Order(PayStrategyEnum.getPay(payKey));
- order.pay();
- }
- }
运行结果
- 使用微信支付
这样,我们就成功的规避了大量的if...else了,爽歪歪!
其实,上面三个版本的代码,是不是觉得很爽,这就是设计模式的强大之处。
PS:关于上面的三个版本,其实我们还可以继续完善,继续重构,感兴趣的你可以去试试如何继续重构。
总结
好了,今天的策略模式就到这里。其实,设计模式在大多数情况下,是不会单独存在的,都是使用多种设计模式混合起来使用的。
策略模式使用的就是面向对象的继承和多态机制,从而实现同一行为在不同场景下不同实现。
最好记的案例:
我们可以使用不同的交通工具去北京玩
坐飞机、坐高铁、坐汽车、开车、骑车。方式很多,你想选哪一条就选那一条。
最后用一句话来总结策略模式:
条条大路通罗马
本文转载自微信公众号「Java后端技术全栈」,可以通过以下二维码关注。转载本文请联系Java后端技术全栈公众号。