热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

再一次实战策略模式,太好用了

前言之前做三方支付系统的时候经常用到策略模式,比如用户会选择不同的支付方式,不同的支付方式又有不同的实现方法或银行接口调用。现在做物联网系统

前言

之前做三方支付系统的时候经常用到策略模式,比如用户会选择不同的支付方式,不同的支付方式又有不同的实现方法或银行接口调用。

现在做物联网系统,基于MQTT协议(TCP层面的协议)来传输数据,根据不同的请求(不同的Topic)处理不同的业务逻辑,也同样用到策略模式。

顿时感觉策略模式非常好用,而且结合Spring的实例化和注入功能,更加方便了。

今天就聊聊基于Spring(Boot)下策略模式的使用。

未使用策略模式时的处理

以物联网为例大家可能不够熟悉,下面就以支付场景为例。比如在支付的过程中我们可能会选择微信支付、支付宝支付或银卡支付。同时,银行卡又分不同银行,这里统一为银行卡。

最简单直接的代码实现形式如下:

public void pay(String payType){if("alipay".equals(payType)){System.out.println("支付宝");}else if("wechatPay".equals(payType)){System.out.println("微信支付");} else if("bank".equals(payType)){System.out.println("银行卡支付");}
}

这样对照设计模式,通常不符合两个原则:单一职责原则和开闭原则。

我们会发现当前类(或方法)不处理了多个业务的功能,一旦任何一个支付方式的修改都可能会影响到其他的支付方式。同时,无法做到对扩展开放,对修改关闭。新增其他支付方式时同样要修改ifelse判断,影响到其他的业务逻辑。

而策略模式通常就是解决这种有很多ifelse处理逻辑,从而提高代码的可维护性、可扩展性和可读性。

策略模式的轮廓

在对上述代码进行改造之前,先来了解一下策略模式的基本组成。

策略模式(Strategy),定义了一组算法,将每个算法都封装起来,并且使它们之间可以互换。

策略模式通常有以下几部分组成:

  • Strategy策略类,用于定义所有支持算法的公共接口;
  • ConcreteStrategy具体策略类,封装了具体的算法或行为,继承于Strategy。
  • Context上下文,用一个ConcreteStrategy来配置,维护一个对Strategy对象的引用;
  • StrategyFactory策略工厂类,用于创建策略类的具体实现;通常此部分可省略,看具体情况。比如后续实例中通过Spring的依赖注入机制实现了策略类的实例化。

用类图来表示(省略策略工厂类)如下图:

image

基于Spring的策略模式实现

目前在实践中通常都是基于Spring的特性来实现策略模式,这里就以此为例来进行讲解。

策略类定义

上面已经提到,策略类用于定义功能的接口,对于支付场景则可命名为PaymentService或PaymentStrategy。

public interface PaymentService {/*** 支付*/PayResult pay(Order order);
}

同时提供该策略类的不同实现类:AlipayService、WeChatPayService、BankPayService。

@Service("alipay")
public class AlipayService implements PaymentService {@Overridepublic PayResult pay(Order order) {System.out.println("Alipay");return null;}
}

@Service("wechatPay")
public class WeChatPayService implements PaymentService {@Overridepublic PayResult pay(Order order) {System.out.println("WeChatPay");return null;}
}

@Service("bank")
public class BankPayService implements PaymentService {@Overridepublic PayResult pay(Order order) {System.out.println("BankPay");return null;}
}

具体实现的实例化,可以通过一个PaymentFactory来进行构建存储,也可以直接利用@Autowired形式注入到Context的List或Map当中。

PaymentFactory的实现如下:

public class PaymentFactory {private static final Map payStrategies &#61; new HashMap<>();static {payStrategies.put("alipay", new AlipayService());payStrategies.put("wechatPay", new WeChatPayService());payStrategies.put("bank", new BankPayService());}public static PaymentService getPayment(String payType) {if (payType &#61;&#61; null) {throw new IllegalArgumentException("pay type is empty.");}if (!payStrategies.containsKey(payType)) {throw new IllegalArgumentException("pay type not supported.");}return payStrategies.get(payType);}
}

通过static静态代码块来初始化对应的策略实现类&#xff0c;然后提供一个getPayment方法&#xff0c;根据支付类型来获取对应的服务。当然&#xff0c;通过static初始化的代码块是单例的无状态的&#xff0c;如果需要有状态的类则getPayment方法&#xff0c;每次都需要new一个新的对象。

public static PaymentService getPayment1(String payType) {if (payType &#61;&#61; null) {throw new IllegalArgumentException("pay type is empty.");}if ("alipay".equals(payType)) {return new AlipayService();} else if ("wechatPay".equals(payType)) {return new WeChatPayService();} else if ("bank".equals(payType)) {return new BankPayService();}throw new IllegalArgumentException("pay type not supported.");
}

Context上下文

Context上下文角色&#xff0c;也叫Context封装角色&#xff0c;起承上启下的作用&#xff0c;屏蔽高层模块对策略、算法的直接访问&#xff0c;封装可能存在的变化。

上面通过工厂的形式创建策略类的实现类&#xff0c;当然也可以直接通过&#64;Autowired注入到Context上下文中。

&#64;Component
public class PaymentStrategy {&#64;Autowiredprivate final Map payStrategies &#61; new HashMap<>();public PaymentService getPayment(String payType) {if (payType &#61;&#61; null) {throw new IllegalArgumentException("pay type is empty.");}if (!payStrategies.containsKey(payType)) {throw new IllegalArgumentException("pay type not supported.");}return payStrategies.get(payType);}
}

上面通过&#64;Autowired注解&#xff0c;将通过&#64;Service实例化的PaymentService实现类&#xff0c;注入到map当中&#xff0c;其中key为实例化类的名称&#xff0c;value为具体的实例化类。

上面的getPayment代码与PaymentFactory中一致。当然&#xff0c;还可以在PaymentStrategy中封装一个pay方法&#xff0c;这样&#xff0c;客户端直接注入PaymentStrategy类调用pay方法即可。

public PayResult pay(String payType,Order order){PaymentService paymentService &#61; this.getPayment(payType);return paymentService.pay(order);
}

改进方案

通过上面的代码基本上已经实现了策略模式&#xff0c;此时当新增加一个支付通道时&#xff0c;已经不用修改PaymentStrategy相关的代码&#xff0c;只用新增一个实现PaymentService接口的类即可。

但在接口定义这里&#xff0c;还是有优化空间的。比如&#xff0c;这里判断是通过Bean的名称来判断的&#xff0c;但某些情况下判断可能比较复杂或可能会同时执行多个Service。此时&#xff0c;就可以对PaymentService接口进行改进&#xff0c;新增一个检验是否支持该功能的判断方法。

public interface PaymentService {boolean isSupport(Order order);/*** 支付*/PayResult pay(Order order);
}

由实现类来具体实现isSupport方法&#xff0c;判断自己支持哪些功能。

同时&#xff0c;上下文类也可以进一步利用Java8提供的Steam特性进行处理&#xff1a;

&#64;Component
public class PaymentStrategy {/*** 此处用&#64;Autowired将所有实例注入为List。*/&#64;Autowiredprivate List paymentServices;public void pay(Order order) {PaymentService paymentService &#61; paymentServices.stream().filter((service) -> service.isSupport(order)).findFirst().orElse(null);if (paymentService !&#61; null) {paymentService.pay(order);} else {throw new IllegalArgumentException("pay type not supported.");}}
}

通过进一步改造&#xff0c;程序变得更加灵活了。

小结

通过上面的代码实现&#xff0c;可以看出接口类只负责业务策略的定义&#xff0c;策略的具体实现可以单独放在实现类中也可以利用Spring的特性进行管理&#xff0c;Context上下文类负责业务逻辑的编排。

通过策略模式&#xff08;或变种&#xff09;的应用&#xff0c;实现了面向接口而非实现编程&#xff0c;满足了职责单一、开闭原则&#xff0c;从而达到了功能上的高内聚低耦合、提高了可维护性、扩展性以及代码的可读性。

最后&#xff0c;对于设计模式&#xff0c;只有在实践中不断的使用采用更加印象深刻。同时&#xff0c;在实现的过程中我们也并不一定非要拘泥于设计模式本身&#xff0c;也可以结合所使用的框架进行变种处理。



程序新视界



公众号“程序新视界”&#xff0c;一个让你软实力、硬技术同步提升的平台&#xff0c;提供海量资料


微信公众号&#xff1a;程序新视界


推荐阅读
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • SpringBoot uri统一权限管理的实现方法及步骤详解
    本文详细介绍了SpringBoot中实现uri统一权限管理的方法,包括表结构定义、自动统计URI并自动删除脏数据、程序启动加载等步骤。通过该方法可以提高系统的安全性,实现对系统任意接口的权限拦截验证。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • CSS3选择器的使用方法详解,提高Web开发效率和精准度
    本文详细介绍了CSS3新增的选择器方法,包括属性选择器的使用。通过CSS3选择器,可以提高Web开发的效率和精准度,使得查找元素更加方便和快捷。同时,本文还对属性选择器的各种用法进行了详细解释,并给出了相应的代码示例。通过学习本文,读者可以更好地掌握CSS3选择器的使用方法,提升自己的Web开发能力。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 本文详细介绍了在ASP.NET中获取插入记录的ID的几种方法,包括使用SCOPE_IDENTITY()和IDENT_CURRENT()函数,以及通过ExecuteReader方法执行SQL语句获取ID的步骤。同时,还提供了使用这些方法的示例代码和注意事项。对于需要获取表中最后一个插入操作所产生的ID或马上使用刚插入的新记录ID的开发者来说,本文提供了一些有用的技巧和建议。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
author-avatar
我们的北京宫
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有