@order注解_别再用ifelse了,用注解去代替他吧

来自公众号:咖啡拿铁

策略模式

经常在网上看到一些名为“别再if-else走天下了”,“教你干掉if-else”等之类的文章,大部分都会讲到用策略模式去代替if-else。策略模式实现的方式也大同小异。主要是定义统一行为(接口或抽象类),并实现不同策略下的处理逻辑(对应实现类)。客户端使用时自己选择相应的处理类,利用工厂或其他方式。

注解实现

本文要说的是用注解实现策略模式的方式,以及一些注意点。
话不多说,还是以最常 见的订单处理为例。首先定义这样一个订单实体类:

@Data

假如对于不同来源(pc端、移动端)的订单需要不同的逻辑处理。项目中一般会有OrderService这样一个类,如下,里面有一坨if-else的逻辑,目的是根据订单的来源的做不同的处理。

@Service

策略模式就是要干掉上面的一坨if-else,使得代码看起来优雅且高大上。
现在就让我们开始干掉这一坨if-else。先总览下结构:

2a962ba28a8ef7f3aa5681d5f3982e9e.png

1.首先定义一个OrderHandler接口,此接口规定了处理订单的方法。
public 

2.定义一个OrderHandlerType注解,来表示某个类是用来处理何种来源的订单。

@Target(ElementType.TYPE)

3.接下来就是实现pc端和移动端订单处理各自的handler,并加上我们所定义的OrderHandlerType注解。

@OrderHandlerType(source = 

4.以上准备就绪后,就是向spring容器中注入各种订单处理的handler,并在OrderService.orderService方法中,通过策略(订单来源)去决定选择哪一个OrderHandler去处理订单。我们可以这样做:

@Service

在OrderService中,维护了一个orderHandleMap,它的key为订单来源,value为对应的订单处理器Handler。通过@Autowired去初始化orderHandleMap(这里有一个lambda表达式,仔细看下其实没什么难度的)。这样一来,OrderService.orderService里的一坨if-else不见了,取而代之的仅仅是两行代码。即,先从orderHandleMap中根据订单来源获取对应的OrderHandler,然后执行OrderHandler.handle方法即可。
这种做法的好处是,不论以后业务如何发展致使订单来源种类增加,OrderService的核心逻辑不会改变,我们只需要实现新增来源的OrderHandler即可,且团队中每人开发各自负责的订单来源对应的OrderHandler即可,彼此间互不干扰。

到此,似乎已经讲完了通过注解实现策略模式,干掉if-else的方法,就这样结束了吗?不,真正的重点从现在才开始。

现在回过头看orderHandleMap这个Map,它的key是订单来源,假如,我们想通过订单来源+订单支付方式这两个属性来决定到底使用哪一种OrderHandler怎么办?比如PC端支付宝支付的订单是一种处理逻辑(PCAliPayOrderHandler),PC端微信支付的订单是另外一种处理逻辑(PCWeChatOrderHandler),对应的还有移动端支付宝支付(MobileAliPayOrderHandler)和移动端微信支付(MobileWeChatOrderHandler)。

这时候我们的key应该存什么呢,可能有人会说,我直接存订单来源+订单支付方式组成的字符串不就行了吗?确实可以,但是如果这时业务逻辑又变了(向pm低头),PC端支付宝支付和微信支付是同一种处理逻辑,而移动端支付宝支付和微信支付是不同的处理逻辑,那情况就变成了PCAliPayOrderHandler和PCWeChatOrderHandler这两个类是同一套代码逻辑。我们干掉了if-else,但却造出了两份相同的代码,这是一个作为有强迫症的程序员所不能容忍的。怎么干掉这两个逻辑相同的类呢?

首先,我们可以回顾下,注解它究竟是个什么玩意?不知道大家有没有注意到定义注解的语法,也就是@interface,与定义接口的语法想比,仅仅多了一个@。翻看jdk,可以找到这么一个接口Annotation,如下

/**
 * The common interface extended by all annotation types.  Note that an
 * interface that manually extends this one does not define
 * an annotation type.  Also note that this interface does not itself
 * define an annotation type.
 *
 * More information about annotation types can be found in section 9.6 of
 * The Java™ Language Specification.
 *
 * The {@link java.lang.reflect.AnnotatedElement} interface discusses
 * compatibility concerns when evolving an annotation type from being
 * non-repeatable to being repeatable.
 *
 * @author  Josh Bloch
 * @since   1.5
 */

开头就表明了,The common interface extended by all annotation types。说的很明白了,其实注解它就是个接口,对,它就是个接口而已,@interface仅仅是个语法糖。那么,注解既然是个接口,就必然会有相应的实现类,那实现类哪里来呢?上述中我们仅仅定义了OrderHandlerType注解,别的什么也没有做。这时候不得不提动态代理了,一定是jdk在背后为我们做了些什么。
为了追踪JVM在运行过程中生成的JDK动态代理类。我们可以设置VM启动参数如下:

true

该参数可以保存所生成的JDK动态代理类到本地。额外说一句,若我们想追踪cglib所生成的代理类,即对应的字节码文件,可以设置参数:

"保存的路径");

添加参数后再次启动项目,可以看到我们项目里多了许多class文件(这里只截取了部分,笔者启动的项目共生成了97个动态代理类。由于我的项目是springboot环境,因此生成了许多如ConditionalOnMissingBean、Configuration、Autowired等注解对应的代理类):

0a9b1604eb93eaf25e4f8c98a9957e70.png

那接下来就是要找到我们所关心的,即jdk为我们自定义的OrderHandlerType注解所生成的代理类。由于jdk生成的动态代理类都会继承Proxy这个类,而java又是单继承的,所以,我们只需要找到实现了OrderHandlerType的类即可。遍历这些类文件,发现$Proxy63.class实现了我们定义的OrderHandlerType注解:

public 

我们知道,jdk动态代理其实现的核心是:

e788bfd9ef6175638f3281271ba43aab.png

也就是这个构造函数的InvocationHandler对象了。那么注解的InvocationHandler是哪个具体实现呢?不难发现就是:
102dc72a454b20681c9bacdb9885ae40.png

这个类里面的核心属性,就是那个 memberValues,我们在使用注解时给注解属性的赋值,都存储在这个map里了。而代理类中的各种方法的实现,实际上是调用了 AnnotationInvocationHandler 里的 invoke 方法。
好了,现在我们知道了注解就是个接口,且通过动态代理实现其中所定义的各种方法。那么回到我们的OrderService,为什么不把key的类型设置为OrderHandlerType?就像这样。
private Map

如此一来,不管决定订单处理器orderhandler的因素怎么变,我们便可以以不变应万变(这不就是我们所追求的代码高扩展性和灵活性么)。那当我们的map的key变成了OrderHandlerType之后,注入和获取的逻辑就要相应改变,注入的地方很好改变,如下:

public 

那获取的逻辑要怎么实现?我们怎么根据order的来源和支付方式去orderHandleMap里获取对应的OrderHandler呢?问题变成了如何关联order的来源和支付方式与OrderHandlerType注解。
还记得刚才所说的注解就是个接口吗,既然是个接口,我们自己实现一个类不就完事了么,这样就把order的来源和支付方式与OrderHandlerType注解关联起来了。说干就干,现在我们有了这么一个类,

public 

在获取对应OrderHandler时我们可以这样写,

public void orderService(Order order) {

看起来没什么问题了,来运行一下。不对劲啊,空指针,那个异常它来了。

70546ea598f9efa7cc5e04a3d79dbb32.png

我们断点打在NPE那一行,
14ba7b1b2fbe56c65af1267c5a2a8ce2.png

一定是姿势不对,漏掉了什么。那我们就来分析下。orderHandleMap中确实注入了所定义的几个OrderHandler(PCAliPayOrderHandler、PCWeChatOrderHandler、MobileAliPayOrderHandler、MobileWeChatOrderHandler),但是get却没有获取到,这是为什么呢?我们来回忆下,map的get方法逻辑,还记得最开始学习java时的hashCode和equals方法吗?
不知道大家注意到没有,map中key对应的类型是$Proxy63(jdk动态代理给我们生成的),跟我们自己实现的OrderHandlerTypeImpl是不同类型的。
梳理下,在Autowied时,我们放进orderHandleMap的key是动态代理类对象,而获取时,自定义了OrderHandlerTypeI实现类OrderHandlerTypeImpl,而又没有重写hashCode和equals方法,才导致从map中没有获取到我们所想要的OrderHandler,那么,我们把实现类OrderHandlerTypeImpl的hashCode和equals这两个方法重写,保持跟动态代理类生成的一样不就行了吗?再回看下动态代理给我们生成的这两个方法,前面说了,注解对应的代理类方法调用实际上都是AnnotationInvocationHandler里面的方法,翻看AnnotationInvocationHandler里面的hashCode和equals方法:
private int hashCodeImpl() {

具体的逻辑也比较简单,就不分析了。那我们就按照AnnotationInvocationHandler中的实现,在我们的OrderHandlerTypeImpl中按照相同的逻辑重写下这两个方法,如下

public 

再次运行看看是否达到我们预期,果不其然,这次可以正常获取到了handler,至此,大功告成。

21c867e2965e9ef9728d01120e888024.png

这样以来,不管以后业务怎么发展,OrderService核心逻辑不会改变,只需要扩展OrderHandler即可。

如果大家觉得这篇文章对你有帮助,你的关注和转发是对我最大的支持,O(∩_∩)O:


●编号1299,输入编号直达本文

●输入m获取文章目录

程序员求职面试

6600eef0157b5a7f8e77f31b4fec198f.png

分享程序员找工作经验

程序员笔试、面试题

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/293355.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

我们能复活恐龙吗?

全世界只有3.14 % 的人关注了爆炸吧知识鲁迅曾经说过:“不会画漫画的段子手不是好英语老师”咳咳~ 图图君就是这样一位专注知识科普的双语漫画家长按二维码带你去图图君家串串门儿我们到底能不能复活恐龙?长按二维码关注回复“恐龙”寻找答案吧在这里你不…

eks volumn s3_威客电竞 深渊联赛S3欧洲区总决赛,Secret成就八连冠

威客电竞,亚洲领先的专业电竞赛事预测平台。DOTA PIT深渊联赛线上赛第三赛季经过为期一周的激烈比赛,Secret战队在11支参赛队伍中一路杀进总决赛,与VP.P战队展开巅峰对决。最终Secret战队3:0完胜VP.P战队斩获DOTA PIT S3欧洲区冠军&#xff0…

初探IdentityServer4(客户端模式)

Oatuth2协议的客户端模式介绍Client Credentials Grant (客户端模式)是Oauth2.0协议中,四种模式自建单的一种。它由两部分构成,客户端和认证服务器。认证服务器确认客户端无误后返回一个token,客户端请求带着token访问…

研究表明:胸部大小其实早已.....

1 这样就能不战而胜了(素材源于网络,侵删)▼2 说好的情比金坚呢?▼3 整挺好,锅都不用洗了(via.豆瓣撕破伤口、)▼4 就是这么自信▼5 什么丝袜这么厉害▼6 好家伙,请问还需要女婿…

Java学习笔记之 IO包 字节流

IO包最重要的五个类和一个接口 File/OutputStream/InputStream(字节流)/Writer/Reader(字符流) 一个接口:Serializable File类: 字节流: OutputStream与InputStream是抽象类 OutputStream是整个IO包中,字节输出流的最大类 Fi…

bootstrap bootstraptable 固定列_1个Excel公式按条件自定义格式显示固定电话号码

点击右上角“关注”,每天免费获取干货教程前面写了很多篇关于数据查询、数据统计、数据分析、公式排错的教程,今天换一个口味,写个关于自定义格式显示数据的教程。因为工作中很多时候都需要按照指定的要求将数据源整理成固定的格式&#xff0…

记一次引入Elasticsearch的系统架构实战

前言我曾经面试安踏的技术岗,当时面试官问了我一个问题:如果你想使用某个新技术但是领导不愿意,你怎么办?对于该问题我相信大家就算没有面试被问到过,现实工作中同事之间的合作也会遇到。 因此从我的角度重新去回答这…

【 Tomcat 】tomcat8.0 调优配置

2019独角兽企业重金招聘Python工程师标准>>> 1、优化内核及TCP连接: fs.file-max 655350  # 系统文件描述符总量 net.ipv4.ip_local_port_range 1024 65535  # 打开端口范围 net.ipv4.tcp_max_tw_buckets 2000  # 设置tcp连接时TIME_WAIT个数 n…

[转]ORACLE 异常错误处理

本文转自:http://www.cnblogs.com/soundcode/archive/2012/01/10/2318385.html 本篇主要内容如下: 5.1 异常处理概念 5.1.1 预定义的异常处理 5.1.2 非预定义的异常处理 5.1.3 用户自定义的异常处理 5.1.4 用户定义的异常处理 5.2 异常错误传播 5.2.1 在…

abb机器人指令手册_第1519课 ABB机器人初级教程

每一位读者都是我分享的动力,感谢大家关注!第15课 系统输入输出与IO信号的关联第16课 示教器可编程按钮的使用第17课 什么是程序数据第18课 程序数据的类型与分类第19课 创建程序数据第15课第16课第17课第18课第19课每一位读者都是我分享的动力&#xff…

Java学习笔记之 IO包 字符流

字符流:Writer/Reader(字符流) 通过子类FileWriter和FileReader实现父类实例化 package iotest; import java.io.File; import java.io.Writer; import java.io.Reader; import java.io.FileWriter; import java.io.FileReader; public class OutputStreamDemo2 { p…

ci框架(二)

自定义SQL语句 当提供的API满足不了我们对SQL语句的要求的时候,我们通常是自己来写SQL语句,CI也提供了比较强大了,能够满足我们需求的一般的sql的API。 $res$…

到底该不该使用存储过程

百度百科对存储过程的定义存储过程(Stored Procedure)是在大型数据库系统中,一组为了完成特定功能的SQL 语句集,它存储在数据库中,一次编译后永久有效,用户通过指定存储过程的名字并给出参数(如…

智能卡门禁管理系统_汉中停车场智能门禁系统简介,保安服务收费,行业知识

首页 > 新闻列表 > 正文作者:佚名 点击数: 5更新时间:2020-11-04 08:21:21 导读:名将安防为您提供汉中停车场智能门禁系统简介,保安服务收费的相关知识与详情: 接警时,系统自动调出预警方案&#xf…

推荐系统中协同过滤算法实现分析

原创博客,欢迎转载,转载请注明:http://my.oschina.net/BreathL/blog/62519 最近研究Mahout比较多,特别是里面协同过滤算法;于是把协同过滤算法的这个实现思路与数据流程,总结了一下,以便以后对系…

老黄的2021

0x01 21年怎么说呢,平淡中带点惊喜。21年在现在的公司待满 2 年了,仔细想想,还真是,这是第一个待满 2 年的公司。这一年,有了很多的挑战,业务上的,技术上的,管理上的都是。下半年开始…

document.elementFromPoint

先说一下这个方法的参数 elemntFromPoint(x,y);//传入坐标值,返回当前页面上包含该坐标点的顶层元素 注意2点,坐标值和顶层元素 先说坐标,因为不同的人理解是不一样的,也就造就了这个方法在不同的浏览器中表现是不一样的&#xff…

iis6 开启options方法_《王者荣耀》暗夜危机什么时候开启 暗夜危机开启时间

导读 王者荣耀神秘关卡暗夜危机怎么进?许多玩家对于这个暗夜危机的关卡还不太熟悉,不知道如何开启,下面就让九游小编为大家带来,王者荣耀暗夜危机开启方法介绍。 王者荣耀神秘关卡暗夜危机怎么进? 峡谷异闻... 王者荣耀…