文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

设计模式在业务系统中的应用

2024-12-03 01:09

关注

本文的重点在于说明工作中所使用的设计模式,为了能够更好的理解设计模式,首先简单介绍一下业务场景。使用设计模式,可以简化代码、提高扩展性、可维护性和复用性。有哪些设计模式,这里就不再介绍了,网上很多,本文只介绍所用到设计模式。

一  线路检查工具

1  意义

为什么需要线路检查工具呢,有以下几个方面的原因:

为什么要把线路检查工具产品化呢,考虑如下:

2  检查项

根据2020年D11进行的数据检查,本次共实现8项,下面列举了4项,如下:

好了,了解了背景知识,下面开始介绍所用到的设计模式,以及为什么要用、怎么用。

二  设计模式

1  模板方法模式+泛型

上述8项数据检查工具,大致的处理流程是类似的,如下:

针对不同的检查工具,只有“线路数据检查”这一步是不一样的逻辑,其他步骤都是相同的,如果每个检查工具都实现这么一套逻辑,必定造成大量的重复代码,维护性较差。

模板方法模式能够很好地解决这个问题。模板方法设计模式包含模板方法和基本方法,模板方法包含了主要流程;基本方法是流程中共用的逻辑,如创建检查任务,结果输出等等。

下图是所 实现的模板方法模式的类继承关系:

分析如下:

1)DataCheckProductService接口为对外提供的服务,dataCheck方法为统一的数据检查接口。

2)AbstractDataCheckProductService是一个抽象类,设定模板,即在dataCheck方法中设定好流程,包括如下:

dataCheck方法为模板方法,runDataCheck方法由各个子类去实现,其他方法是基本方法。还有一些其他方法,是各个检查工具都需要使用的,所以就放在了父类中。

3) CheckSupplierAndCodeMappingService类、CheckLandingCoverAreaService类和CheckAncPathNoServiceService类为具体的检查工具子类,必须实现runDataCheck方法。

因为不同检查项检的查结果的格式是不一样的,所以使用了泛型,使得可以兼容不同的检查结果。

简化的代码如下:

  1.  
  2. public interface DataCheckProductService { 
  3.      
  4.    BaseResult dataCheck(DataCheckRequestDTO requestDTO); 
  5.  
  6.  
  7.  
  8. public abstract class AbstractDataCheckProductService implements DataCheckProductService { 
  9.      
  10.     @Override 
  11.     public  BaseResult dataCheck(DataCheckRequestDTO requestDTO){ 
  12.         try
  13.             //1. 参数合法性检查 
  14.             Pair paramCheckResult = commonParamCheck(requestDTO); 
  15.             if(!paramCheckResult.getLeft()){ 
  16.                 return BaseResult.ofFail(paramCheckResult.getRight()); 
  17.             } 
  18.              
  19.             //2. 创建导出任务 
  20.             String fileName = createFileName(requestDTO); 
  21.             RouteTaskRecordDO taskRecordDO = createTaskRecord(fileName, requestDTO.getUserName()); 
  22.  
  23.  
  24.             //3. 进行数据检查 
  25.             List resultList = Collections.synchronizedList(new ArrayList<>()); 
  26.             runDataCheck(resultList, requestDTO); 
  27.  
  28.  
  29.             //4. 写入文件 
  30.             String ossUrl = uploadToOSS(fileName,resultList); 
  31.             //5. 更新任务为完成状态 
  32.             updateRouteTask(taskRecordDO.getId(),DDportTaskStatus.FINISHED.intValue(), resultList.size()-1,"",ossUrl); 
  33.  
  34.  
  35.             return BaseResult.ofSuccess(taskRecordDO.getId()); 
  36.         }catch (Exception e){ 
  37.             LogPrinter.errorLog("dataCheck-error, beanName="+getBeanName(),e); 
  38.             return BaseResult.ofFail(e.getMessage()); 
  39.         } 
  40.     } 
  41.  
  42.  
  43.       
  44.     public abstract  void runDataCheck(List resultList, DataCheckRequestDTO requestDTO); 
  45.  
  46.  
  47.  
  48. public class CheckSupplierAndCodeMappingService extends AbstractDataCheckProductService{ 
  49.     @Override 
  50.     public  void runDataCheck(List resultList, DataCheckRequestDTO requestDTO){ 
  51.         //自己的检查逻辑 
  52.     } 
  53.  
  54.  
  55.  
  56. public class CheckLandingCoverAreaService extends AbstractDataCheckProductService{ 
  57.     @Override 
  58.     public  void runDataCheck(List resultList, DataCheckRequestDTO requestDTO){ 
  59.         //自己的检查逻辑 
  60.     } 
  61.  
  62.  
  63.  
  64. public class CheckAncPathNoServiceService extends AbstractDataCheckProductService{ 
  65.     @Override 
  66.     public  void runDataCheck(List resultList, DataCheckRequestDTO requestDTO){ 
  67.         //自己的检查逻辑 
  68.     } 

使用模板方法模式的好处是:

2  策略模式

之所以会用到策略模式,是因为一些检查工具写完之后,发现跑出来的结果数据太多,有几万、几十万等等,一方面,检查比较耗时,结果文件会很大,下载耗时;另一方面,这么多数据给到业务同学,他们也很难处理和分析,也许他们只是想看一下总体情况,并不想看具体的到哪个地方的线路。为此,在原先方案设计的基础上,增加了“统计信息”的选项,让用户可以自行选择“详细信息”还是“统计信息”,对应到页面上就是一个单选框,如下:

现在增加了一种检查方式,今后是否还会有其他的检查方式?完全有可能的。所以得考虑到扩展性,便于后面同学增加新的检查方式。

此外,还有一种场景也可以使用策略模式,那就是业务系统中有很多业务网络,不同网络之间有一些差异;本次所实现的检查工具,有几个涉及到多个网络,今后可能会涉及到所有网络。

综合以上两种场景,最合适的就是策略模式了。“详细信息”和“统计信息”各采用一种策略,不同网络使用不同的策略,既便于代码理解,又便于后续扩展。

“详细信息”和“统计信息”两种检查结果的策略类图如下:

解析:

简化的代码如下:

  1.  
  2. public interface CompareModeStrategy { 
  3.      
  4.      List doHandle(List list, DataCheckRequestDTO requestDTO); 
  5.  
  6.  
  7.  
  8. public abstract class AbstractCompareModeStrategy implements CompareModeStrategy { 
  9.     //子类的共用方法,可以放在此类中 
  10.  
  11.  
  12.  
  13. public class LandingCoverAreaDetailStrategy extends AbstractCompareModeStrategy{ 
  14.     @Override 
  15.     public  List doHandle(List list, DataCheckRequestDTO requestDTO){ 
  16.         List resultList = new ArrayList<>(); 
  17.     //检查结果处理逻辑 
  18.         return resultList; 
  19.     } 
  20.  
  21.  
  22.  
  23. public class LandingCoverAreaStatisticsStrategy extends AbstractCompareModeStrategy{ 
  24.     @Override 
  25.     public  List doHandle(List list, DataCheckRequestDTO requestDTO){ 
  26.         List resultList = new ArrayList<>(); 
  27.     //检查结果处理逻辑 
  28.         return resultList; 
  29.     } 
  30.  
  31.  
  32.  
  33. public class SupplierAndCodeMappingDetailStrategy extends AbstractCompareModeStrategy{ 
  34.     @Override 
  35.     public  List doHandle(List list, DataCheckRequestDTO requestDTO){ 
  36.         List resultList = new ArrayList<>(); 
  37.     //检查结果处理逻辑 
  38.         return resultList; 
  39.     } 
  40.  
  41.  
  42.  
  43. public class SupplierAndCodeMappingStatisticsStrategy extends AbstractCompareModeStrategy{ 
  44.     @Override 
  45.     public  List doHandle(List list, DataCheckRequestDTO requestDTO){ 
  46.         List resultList = new ArrayList<>(); 
  47.     //检查结果处理逻辑 
  48.         return resultList; 
  49.     } 

同样,不同网络的处理策略类图如下:

代码与上面类似,就不展示了。

使用策略模式的好处是:

3  工厂模式

工厂模式解决的是bean的生产问题,简单工厂模式根据入参生产不同的bean,普通工厂模式针对每个bean都构建一个工厂,此两者各有优劣,看需要。本方案采用的是简单工厂模式。

之所以使用工厂模式,是因为有太多的bean需要构造,如果在业务逻辑中构造各种bean,则会显得凌乱和分散,所以需要一个统一生成bean的地方,更好地管理和扩展。

本方案中主要有三类bean需要工厂来生成:

所以,使用三个工厂分别构造这三种类型的bean。另外,因为每个bean主要的功能都在方法中,不涉及类变量的使用,所以可以利用spring容器生成的bean,而不是我们自己new出来,这样就使得bean可以重复使用。因此,这里的工厂只是bean的决策(根据参数决定使用哪个bean),不用自己new了。

三个工厂分别如下:

这三个工厂的代码类似,这里就以CompareModeStrategyFactory为例,简化的代码如下:

  1.  
  2. @Service 
  3. public class CompareModeStrategyFactory { 
  4.  
  5.  
  6.      
  7.     @Resource 
  8.     private LandingCoverAreaDetailStrategy landingCoverAreaDetailStrategy; 
  9.     @Resource 
  10.     private SupplierAndCodeMappingDetailStrategy supplierAndCodeMappingDetailStrategy; 
  11.  
  12.  
  13.      
  14.     @Resource 
  15.     private LandingCoverAreaStatisticsStrategy landingCoverAreaStatisticsStrategy; 
  16.     @Resource 
  17.     private SupplierAndCodeMappingStatisticsStrategy supplierAndCodeMappingStatisticsStrategy; 
  18.  
  19.  
  20.      
  21.     public CompareModeStrategy getCompareModeStrategy(DataCheckProductEnum productEnum, DataCompareModeEnum modeEnum){ 
  22.         CompareModeStrategy compareModeStrategy = null
  23.         switch (modeEnum){ 
  24.             case DETAIL_INFO: 
  25.                 compareModeStrategy = getDetailCompareModeStrategy(productEnum); 
  26.                 break
  27.             case STATISTICS_INFO : 
  28.                 compareModeStrategy = getStatisticsCompareModeStrategy(productEnum); 
  29.                 break
  30.             default:; 
  31.         } 
  32.         return compareModeStrategy; 
  33.     } 
  34.      
  35.     private CompareModeStrategy getDetailCompareModeStrategy(DataCheckProductEnum productEnum){ 
  36.         CompareModeStrategy compareModeStrategy = null
  37.         switch (productEnum){ 
  38.             case CHECK_LANDING_COVER_AREA: 
  39.                 compareModeStrategy = landingCoverAreaDetailStrategy; 
  40.                 break
  41.             case CHECK_SUPPLIER_AND_CODE_MAPPING: 
  42.                 compareModeStrategy = supplierAndCodeMappingDetailStrategy; 
  43.                 break
  44.             default:; 
  45.         } 
  46.         return compareModeStrategy; 
  47.     } 
  48.      
  49.     private CompareModeStrategy getStatisticsCompareModeStrategy(DataCheckProductEnum productEnum){ 
  50.         CompareModeStrategy compareModeStrategy = null
  51.         switch (productEnum){ 
  52.             case CHECK_LANDING_COVER_AREA: 
  53.                 compareModeStrategy = landingCoverAreaStatisticsStrategy; 
  54.                 break
  55.             case CHECK_SUPPLIER_AND_CODE_MAPPING: 
  56.                 compareModeStrategy = supplierAndCodeMappingStatisticsStrategy; 
  57.                 break
  58.             default:; 
  59.         } 
  60.         return compareModeStrategy; 
  61.     } 

使用工厂模式的好处是:

4  “代理模式”

这个代理模式是打着双引号的,因为不是真正的代理模式,只是从实现方式上来说,具有代理模式的意思。为什么需要用到代理模式?是因为类太多了,业务逻辑分散在各个类中,有的在模板子类中,有的在网络策略中,有的在结果输出格式策略中,而这些业务逻辑都需要多线程执行和异常捕获。如果有个代理类,能够收口这些处理逻辑,只需增加前置多线程处理和后置异常处理即可。

Java语言中的函数式编程,具备这种能力。所谓函数式编程,是指能够将方法当做参数传递给方法,前面“方法”是业务逻辑,后面“方法”是代理,将业务逻辑传递给代理,就实现了统一收口的目的。

能够实现此功能的接口有四个,分别是:Consumer、Supplier、Predicate与Function,怎么使用可以网上查询。本方案使用的是Consumer,因为它是用来消费的,即需要传入一个参数,没有返回值,符合本方案的设计。

简化后的代码如下:

  1. @Service 
  2. public class CheckLandingCoverAreaService extends AbstractDataCheckProductService { 
  3.     @Override 
  4.     public  void runDataCheck(List resultList, DataCheckRequestDTO requestDTO){ 
  5.         dataCheckUtils.parallelCheckByFromResCodes(requestDTO,requestDTO.getFromResCodeList(),fromResCode->{ 
  6.             ExpressNetworkQuery query = new ExpressNetworkQuery(); 
  7.             query.setNs(NssEnum.PUB.getId()); 
  8.             query.setStatus(StatusEnum.ENABLE.getId()); 
  9.             query.setGroupNameList(requestDTO.getGroupNameList()); 
  10.             query.setBrandCodeList(requestDTO.getBrandCodeList()); 
  11.             query.setFromResCode(fromResCode); 
  12.             List masterExpressNetworkDOS = tmsMasterExpressNetworkService.queryExpressNetworkTimeList(query); 
  13.             startCompareWithAnc(resultList,requestDTO,masterExpressNetworkDOS,fromResCode,solutionCodeMap); 
  14.         }); 
  15.     } 
  16.  
  17.  
  18. @Service 
  19. public class DataCheckUtils { 
  20.      
  21.     public  void parallelCheckByFromResCodes(DataCheckRequestDTO requestDTO, List fromResCodeList, Consumer checkOperation){ 
  22.         List futureList = Collections.synchronizedList(new ArrayList<>()); 
  23.         fromResCodeList.forEach(fromResCode->{ 
  24.             CompletableFuture future = CompletableFuture.runAsync(() -> { 
  25.                 try
  26.                     checkOperation.accept(fromResCode); 
  27.                 }catch (Exception e){ 
  28.                     LogPrinter.errorLog("parallelCheckByFromResCodes-error, taskId="+requestDTO.getTaskId(),e); 
  29.                     recordErrorInfo(requestDTO.getTaskId(),e); 
  30.                 } 
  31.             }, DATA_CHECK_THREAD_POOL); 
  32.             futureList.add(future); 
  33.         }); 
  34.         //等待所有线程结束 
  35.         futureList.forEach(future->{ 
  36.             try
  37.                 future.get(); 
  38.             }catch (Exception e){ 
  39.                 LogPrinter.errorLog("parallelCheckByFromResCodes-future-get-error",e); 
  40.             } 
  41.         }); 
  42.     } 

可以看出,Consumer所代表的就是一个方法,将此方法作为parallelCheckByFromResCodes方法的一个参数,在parallelCheckByFromResCodes中进行多线程和异常处理,既能统一收口,又大大减少了重复代码。

代理模式的好处是:

5  其他

像结果输出格式策略模式那样,虽然AbstractCompareModeStrategy没有实际的业务逻辑,但仍然把它作为一个基类,目的是所有子类共用的逻辑或方法,能够放在此类中,减少代码量,提升维护性。

但是有的时候,不是继承自同一个基类的子类们,仍然要共用一些逻辑或方法(如parallelCheckByFromResCodes方法),但Java语言限制一个类只能继承一个基类,怎么办呢?简单的办法就是把这些共用逻辑或方法放到一个工具类(如DataCheckUtils)中。

三  思考&感悟

在做这个项目的过程中,刚开始没有很好的设计,也没有想的很全面,导致代码改了又改,虽然耽误点时间,但觉得是值得的。总结以下几点:

 

 

 

来源:阿里技术内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯