点外卖,让我想起了 策略模式

开发 前端
策略模式:英文为Strategy Pattern,是指定义了算法家族、分别封装起来,让他们之间可以相互替换,此设计模式让算法的变化不会影响到使用算法的用户。

[[402404]]

今天给大家分享的是策略模式,具体内容大纲如下:

生活案例

在这互联网时代,尤其是在城市中,有一帮骑着电瓶车,穿梭在大街小巷中,这帮人就是外卖小哥。

[[402405]]

对于点外卖,我也点过不少。有一次,外卖下单的时候,我突然联想到了一个设计模式---策略模式。

策略模式是个啥?

策略模式:英文为Strategy Pattern,是指定义了算法家族、分别封装起来,让他们之间可以相互替换,此设计模式让算法的变化不会影响到使用算法的用户。

英文

Define a family of algorithms,encapsulate each one,and make them interchangeable.

大致意思:定义一组算法,将每个算法都封装起来,并且使它们之间可以互换。

在策略模式中,一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。

策略模式通用代码

java代码实现如下:

  1. class Client { 
  2.  
  3.     //抽象策略类 Strategy 
  4.     interface IStrategy { 
  5.         void algorithm(); 
  6.     } 
  7.  
  8.     //具体策略类 ConcreteStrategy 
  9.     static class ConcreteStrategyA implements IStrategy { 
  10.  
  11.         @Override 
  12.         public void algorithm() { 
  13.             System.out.println("Strategy A"); 
  14.         } 
  15.     } 
  16.  
  17.     //具体策略类 ConcreteStrategy 
  18.     static class ConcreteStrategyB implements IStrategy { 
  19.  
  20.         @Override 
  21.         public void algorithm() { 
  22.             System.out.println("Strategy B"); 
  23.         } 
  24.     } 
  25.  
  26.     //上下文环境 
  27.     static class Context { 
  28.         private IStrategy mStrategy; 
  29.  
  30.         public Context(IStrategy strategy) { 
  31.             this.mStrategy = strategy; 
  32.         } 
  33.  
  34.         public void algorithm() { 
  35.             this.mStrategy.algorithm(); 
  36.         } 
  37.     } 
  38.      
  39.    public static void main(String[] args) { 
  40.         //选择一个具体策略 
  41.         IStrategy strategy = new ConcreteStrategyA(); 
  42.         //来一个上下文环境 
  43.         Context context = new Context(strategy); 
  44.         //客户端直接让上下文环境执行算法 
  45.         context.algorithm(); 
  46.     } 

从上面的通用代码,我们可以得知其UML图。

策略模式UML图

策略模式中的角色

从 UML 类图中,我们可以看到,策略模式主要包含三种角色:

  • 上下文角色(Context):用来操作策略的上下文环境,屏蔽高层模块(客户端)对策略,算法的直接访问,封装可能存在的变化;
  • 抽象策略角色(Strategy):规定策略或算法的行为;
  • 具体策略角色(ConcreteStrategy):具体的策略或算法实现;

策略模式优缺点

优点

  • 策略模式符合开闭原则
  • 避免使用多重转换语句,比如:if...else、switch语句。
  • 使用策略模式可以提高算法的保密性和安全性

缺点

  • 客户端必须知道所有策略,并且自行决定使用哪一种策略
  • 代码中产生非常多的策略类,增加后期维护难度

策略模式使用场景

在日常开发中,策略模式适用于以下三种场景:

  • 针对同一类型问题,有多重处理方式,每一种都能独立解决问题。
  • 算法需要自由切换的场景。
  • 需要屏蔽算法规则的场景

这个说起来,还是不太好理解。

下面,我们就来使用生活案例来实现,让大家知道策略模式到底是怎么使用的。

支付案例代码重构,三个版本

外面下单,选择支付方式的时候,我觉这个功能,我们可以模仿着使用策略模式来实现一下。下面我们通过三个版本的迭代来实现,很有意思的。

第一版

先定义一个抽象类Pay:

  1. //定义抽象类,我们可以把一些共用功能放在抽象类里实现 
  2. //比如:可用余额和本次支付金额进行比较,统一返回“支付失败” 
  3. public abstract class  Pay { 
  4.     abstract  void doPay(); 

下面模拟三种支付方式:

  1. public class AliPay extends Pay { 
  2.     @Override 
  3.     public void doPay() { 
  4.         System.out.println("使用支付宝支付"); 
  5.     } 
  6. public class UnionPay extends Pay { 
  7.     @Override 
  8.     public void doPay() { 
  9.         System.out.println("使用银联支付"); 
  10.     } 
  11. public class WechatPay extends Pay { 
  12.     @Override 
  13.     public void doPay() { 
  14.         System.out.println("使用微信支付"); 
  15.     } 

我们再来进行支付:

  1. public class PayTest { 
  2.     public static void main(String[] args) { 
  3.         //把选择权交给了用户 
  4.         Order order = new Order(new WechatPay()); 
  5.         order.pay();* 
  6.     } 

运行结果:

  1. 使用微信支付 

这样我们就使用策略模式实现了简单版本的支付,但是其中有个很不爽的地方,就是每次还得自己手工new一个支付方式的对象。鉴于此,我们对第一版进行重构。

第二版

前面的实现都不变,变化的是发起支付的时候,只要前端传一个key过来就可以了,实现如下:

  1. public class PayTest { 
  2.     public static void main(String[] args) {  
  3.  
  4.         String payKey = "Wechat"
  5.         Order order = null
  6.         //通过判断前端传过来的key,判断使用哪种支付方式 
  7.         if (payKey.equals("Ali")) { 
  8.             order = new Order(new AliPay()); 
  9.         } else if (payKey.equals("Wechat")) { 
  10.             order = new Order(new WechatPay()); 
  11.         } else if (payKey.equals("union")) { 
  12.             order = new Order(new UnionPay()); 
  13.         }else { 
  14.             //给出一个默认方式 
  15.             order = new Order(new WechatPay()); 
  16.         } 
  17.         order.pay(); 
  18.     } 

运行结果

  1. 使用微信支付 

这样我们就实现了,通过前端传过来的key,然后选出对应的支付方式。但是问题又来了,如果支付方式不断增多,那这里的if...else岂不是会越来越多吗?后续维护成本不是越来越大吗?

于是,第三版就有了。

第三版

在第二版中,会出现大量的if...else,会给后续的代码维护带来不便,于是在这一版中,我们对其进行重构,引入了注册式单例模式。

  1. import java.util.HashMap; 
  2. import java.util.Map; 
  3.  
  4. public enum PayStrategyEnum { 
  5.     ALI_PAY("Ali"), 
  6.     WECHAT_PAY("Wechat"), 
  7.     UNION_PAY("union"), 
  8.     //默认使用微信支付 
  9.     DEFAULT_PAY("Wechat"); 
  10.  
  11.     private String key
  12.  
  13.     PayStrategyEnum(String key) { 
  14.         this.key = key
  15.     } 
  16.  
  17.     private static final Map<String, Pay> payKeyMap = new HashMap(); 
  18.  
  19.     static { 
  20.         payKeyMap.put(ALI_PAY.key, new AliPay()); 
  21.         payKeyMap.put(WECHAT_PAY.key, new WechatPay()); 
  22.         payKeyMap.put(UNION_PAY.key, new UnionPay()); 
  23.         payKeyMap.put(DEFAULT_PAY.key, new WechatPay()); 
  24.     } 
  25.  
  26.     public static Pay getPay(String payKey) { 
  27.         if (!payKeyMap.containsKey(payKey)) { 
  28.             return payKeyMap.get(DEFAULT_PAY.key); 
  29.         } 
  30.         return payKeyMap.get(payKey); 
  31.     } 

然后,在订单支付的时候就变成了这样了:

  1. public class PayTest { 
  2.     public static void main(String[] args) {  
  3.         String payKey = "Wechat";  
  4.         Order order = new Order(PayStrategyEnum.getPay(payKey)); 
  5.         order.pay(); 
  6.     } 

运行结果

  1. 使用微信支付 

这样,我们就成功的规避了大量的if...else了,爽歪歪!

其实,上面三个版本的代码,是不是觉得很爽,这就是设计模式的强大之处。

PS:关于上面的三个版本,其实我们还可以继续完善,继续重构,感兴趣的你可以去试试如何继续重构。

总结

好了,今天的策略模式就到这里。其实,设计模式在大多数情况下,是不会单独存在的,都是使用多种设计模式混合起来使用的。

策略模式使用的就是面向对象的继承和多态机制,从而实现同一行为在不同场景下不同实现。

最好记的案例:

我们可以使用不同的交通工具去北京玩

坐飞机、坐高铁、坐汽车、开车、骑车。方式很多,你想选哪一条就选那一条。

最后用一句话来总结策略模式:

条条大路通罗马

本文转载自微信公众号「Java后端技术全栈」,可以通过以下二维码关注。转载本文请联系Java后端技术全栈公众号。

 

责任编辑:武晓燕 来源: Java后端技术全栈
相关推荐

2022-04-29 21:37:34

漏洞网络安全网络攻击

2020-07-20 07:48:53

单例模式

2024-12-09 09:40:00

策略模式Java

2013-11-26 16:09:34

Android设计模式

2021-03-08 10:27:42

CIO销售策略IBM

2021-06-09 08:53:34

设计模式策略模式工厂模式

2015-09-08 13:39:10

JavaScript设计模式

2021-04-26 06:03:07

Reacterror前端

2011-07-20 14:04:42

.NET策略模式

2012-08-30 09:07:33

设计模式

2021-06-17 06:19:20

存储SQL数据库

2024-10-06 12:56:36

Golang策略设计模式

2014-12-29 10:39:16

JS

2024-01-29 12:22:07

设计模式策略模式

2017-07-07 10:55:14

数据库MongoDB设计模式

2012-12-19 14:58:28

2016-11-27 20:43:26

云计算迭代

2023-06-26 07:10:51

2024-01-22 11:48:20

策略模式开发
点赞
收藏

51CTO技术栈公众号