zoukankan      html  css  js  c++  java
  • 关于我用设计模式对公司代码重构的这件事

    (所有代码皆是脱敏后的伪代码)

    1. 基本需求:

    这是一个单独的服务,主要负责顺序执行十几个计算类的任务Task。需要统计每天Task的执行情况,卡在哪里,做一些手动重试和手动流程回滚等操作。

    每个Task正常是按时间约定每天定时按顺序执行,前一个Task失败会导致后续的Task都不能执行。Task执行异常会有相关出错误原因,时间等相关日志留存。

    其中有两个状态,当日的总的流程状态我们用 totalStatus表示, 每一个任务自己的状态我们用 taskStatus来表示。

    2.原始实现

    看一下之前的设计

    非常的简单,一个任务就是一个Task类,每个类中有一个定时任务的开关schedule(),

    schedule()方法仅是作为定时任务的入口,调用的是audit()方法来做具体的计算,包括调用其它服务查数据,做大量的计算,和生成或拉取文件,最后入库的操作。

    而reAudit()方法是作为手动调用的入口,把上边那些计算操作都走一遍,是的 也在代码里重复写了一遍。。。仅是有入参判断等等略微差异。

    并且audit()和reAudit()方法中都有一个大大的try catch包裹住,用来捕捉异常情况然后做变更任务的状态,日志留痕等操作。

    每个Task类的伪代码大概就是这样,reAudit()几乎和audit()是一模一样的,这里就不贴了

    3. 暴露的问题

    1 每个任务都会写类似重复的逻辑代码,主要那四步骤

    2 每个任务都有try catch这种处理,处理错误状态以及错误信息

    3 扩展性不好,如果新添加一个任务,所有的这些逻辑都要重复写一遍

    4 手动执行相关接口,非常容易响应超时

    5 流程回滚是在代码里一个一个调用的(如下图)

    4.思考与重构

    1.每个类中都有的相似代码甚至重复代码,找到他们的共同点抽象出来

    很容易让人想到模板模式,把程序化的流程执行这些东西抽象到父类,子类只需要实现具体细节。

    将每个Task中共有的步骤1,2,3,4提到抽象层,具体实现由各个Task继承并实现

    audit()与reAudit()方法也提到抽象层,其他的暂时都没动。

    2.此时每个Task类的代码已经很清爽了,仅有一个定时任务方法和四个步骤的具体实现

    但是抽象父类audit()方法优点冗长,我觉得他做了很多不该做的事

    Task的职责应该仅仅为了任务的执行逻辑而服务,不应该带状态处理,日志打印这些操作。

    对于这种方法执行前后需要做一些操作的逻辑,很容易让人在设计模式中找到我们的 代理模式

    我这里使用的是静态代理,完全可以满足需求

    把audit()抽象到接口,使代理类TaskProxy和AbstractTask都继承该接口

    很标准的静态代理,代码易懂,我就不墨迹了

    3. 到此代码就已经很清爽了,如果新增加一个任务只需要继承AbstractTask,实现do1(),do2(),do3(),do4()就可以了,其他的统统省掉

    还有一个问题,

    当清算整体回滚操作时,是在代码里写死的执行顺序 (可以翻一下上边的rollback()方法)

    这种按照顺序一个接一个的执行有没有容易想到类似于 责任链模式

    我们可不可以把每个Task串在一起串成一个链,回滚的话就按链的顺序执行就好了。

    (根据责任链模式,应该前一个Task中存下一个Task,上一个执行完直接交给下一个。我觉得这样实现不太好,如果哪天产品经理一拍脑门Task的顺序变了或者其中增减了Task,改起来挺难受的)

    (莫不如直接记录每个Task在顺序序号,直接按序号执行就完事了,要改也只需要改序号。)

    由于每个Task都是基于SpringIOC注入的,我们直接依赖Spring容器这个大工厂,构建一个小工厂。

    创建了一个枚举类,记录了每个Task的生辰八字,其中包括各个Task执行顺序编号和在Spring中的Bean名称

    我们创建一个新的类TaskManager,用来管理这些Task。

    在初始化的时候,在Spring容器中找到这些Task实例对象,根据按照顺序存成一个链表,

    而rollbak()方法就直接for循环遍历链表,把Task交给代理类,执行audit()方法就可以了。

    各司其职,

    TaskManager负责获取并管理Task对象,Task顺序链表。

    TaskProxy负责Task的执行前后的增强,更新状态,异常捕获并日志留存。

    各个Task仅是负责底层实现逻辑,实际的计算啊,入库等操作。

    4.最后手动直接Task操作接口响应超时的问题

    直接用线程池改成异步执行就好了。单例模式获取到线程池,实现异步执行,就不赘述了。

      

    5. 总结

    本次利用了几种设计模式的思想对已有代码进行了简单的重构,感觉收获还是挺多的。

    至少让代码干净简洁了不少,更易读和可维护。  

         

    可以明显的看到整个task包的大小缩了三分之二,其中每个Task类从二三百行 缩到了四五十行,实现了瘦身

    还是希望大家平时在开发过程中可以多一些思考,即便做不到代码优雅,但求尽量简洁易读。

    至于那种一打开上千行的类,一个方法几百行,公司给我配的2核cpu的虚拟机是真的顶不住。

    技术平平,水平有限,如有任何漏洞还请指正

  • 相关阅读:
    Linux下部署svn服务
    eclipse3.3.2在CentOS5.4下启动时崩溃的解决方法
    Cassandra配置
    Hadoop系列相关优秀网站收集
    ul li 制作导航栏 程序员
    一.Java访问权限饰词(access specifiers) 程序员
    Java程序员应该了解的10个面向对象设计原则 程序员
    MyEclipse里更改字体大小和快捷建的设置 程序员
    JFileChooser 为用户选择文件提供了一种简单的机制 程序员
    JavaScript时间函数总结 程序员
  • 原文地址:https://www.cnblogs.com/ttaall/p/15815290.html
Copyright © 2011-2022 走看看