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

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

2  策略模式

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


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

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

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

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


解析:

  • CompareModeStrategy对外提供统一的结果处理接口doHandle,策略子类必须实现此接口。


  • SupplierAndCodeMappingStatisticsStrategy和SupplierAndCodeMappingDetailStrategy是检查配送资源和编码映射一致性的两种结果信息方式,前者为统计方式,后者为详细方式。


  • LandingCoverAreaStatisticsStrategy和LandingCoverAreaDetailStrategy是检查落地配覆盖范围的两种结果信息方式,前者为统计方式,后者为详细方式。


  • 那AbstractCompareModeStrategy是干什么用的?它是一个抽象类,负责承接所有策略子类共用的一些方法。


简化的代码如下:



















































































/** * 检查结果策略对外接口 * @author xxx * @since xxx * */public interface CompareModeStrategy {    /**     * 具体操作     *     * @param list     * @param requestDTO     * @return 结果集     * */    <T> List<T> doHandle(List<CompareBO> list, DataCheckRequestDTO requestDTO);}
/** * 策略公共父类 * * @author xxx * @since xxx * @apiNote 主要是将子类共用方法和成员抽离出来 * */public abstract class AbstractCompareModeStrategy implements CompareModeStrategy {    //子类的共用方法,可以放在此类中}
/** * 检查落地配覆盖范围 详细信息 策略类 * @author xxx * @since xxx * */public class LandingCoverAreaDetailStrategy extends AbstractCompareModeStrategy{    @Override    public <T> List<T> doHandle(List<CompareBO> list, DataCheckRequestDTO requestDTO){        List<T> resultList = new ArrayList<>();    //检查结果处理逻辑        return resultList;    }}
/** * 检查落地配覆盖范围 统计信息 策略类 * @author xxx * @since xxx * */public class LandingCoverAreaStatisticsStrategy extends AbstractCompareModeStrategy{    @Override    public <T> List<T> doHandle(List<CompareBO> list, DataCheckRequestDTO requestDTO){        List<T> resultList = new ArrayList<>();    //检查结果处理逻辑        return resultList;    }}
/** * 检查配送资源和编码映射一致 详细信息 策略类 * @author xxx * @since xxx * */public class SupplierAndCodeMappingDetailStrategy extends AbstractCompareModeStrategy{    @Override    public <T> List<T> doHandle(List<CompareBO> list, DataCheckRequestDTO requestDTO){        List<T> resultList = new ArrayList<>();    //检查结果处理逻辑        return resultList;    }}
/** * 检查配送资源和编码映射一致 统计信息 策略类 * @author xxx * @since xxx * */public class SupplierAndCodeMappingStatisticsStrategy extends AbstractCompareModeStrategy{    @Override    public <T> List<T> doHandle(List<CompareBO> list, DataCheckRequestDTO requestDTO){        List<T> resultList = new ArrayList<>();    //检查结果处理逻辑        return resultList;    }}


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



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

使用策略模式的好处是:


  • 提高代码扩展性,后续增加别的结果格式或别的网络处理逻辑,可以在不修改其他策略的情况下直接新增。


  • 提高代码可读性,取代了if...else,条理清晰。


  • 不同系列采用不同的策略,策略与策略之间可以嵌套使用,形成策略的叠加效用。


3  工厂模式

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

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

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

  • 模板方法模式中所用到的子类。


  • 检查结果格式策略中所用到的子类。


  • 不同网络处理策略中所用到的子类。


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

三个工厂分别如下:

  • DataCheckProductFatory:由getDataCheckProductService方法根据输入参数决策使用哪个数据检查工具。


  • CompareModeStrategyFactory:用于决策使用哪种格式输出,因为将输出策略分为了2类(详细信息和统计信息),所以需要传入两个参数才能决定使用哪种策略。


  • DataCheckNetworkStrategyFactory:用于决策使用哪种网络处理策略,因为将策略分为了2类(4PL网络和其他网络),所以需要传入两个参数才能决定使用哪种策略。



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






































































/** * 比对结果集方式 * @author xxx * @since xxx * */@Servicepublic class CompareModeStrategyFactory {
    /************************ 详细结果的bean  **************************/    @Resource    private LandingCoverAreaDetailStrategy landingCoverAreaDetailStrategy;    @Resource    private SupplierAndCodeMappingDetailStrategy supplierAndCodeMappingDetailStrategy;
    /************************ 统计结果的bean  **************************/    @Resource    private LandingCoverAreaStatisticsStrategy landingCoverAreaStatisticsStrategy;    @Resource    private SupplierAndCodeMappingStatisticsStrategy supplierAndCodeMappingStatisticsStrategy;
    /**     * 获取比对结果的策略     * */    public CompareModeStrategy getCompareModeStrategy(DataCheckProductEnum productEnum, DataCompareModeEnum modeEnum){        CompareModeStrategy compareModeStrategy = null;        switch (modeEnum){            case DETAIL_INFO:                compareModeStrategy = getDetailCompareModeStrategy(productEnum);                break;            case STATISTICS_INFO :                compareModeStrategy = getStatisticsCompareModeStrategy(productEnum);                break;            default:;        }        return compareModeStrategy;    }    /**     * 获取 信息信息 策略对象     * */    private CompareModeStrategy getDetailCompareModeStrategy(DataCheckProductEnum productEnum){        CompareModeStrategy compareModeStrategy = null;        switch (productEnum){            case CHECK_LANDING_COVER_AREA:                compareModeStrategy = landingCoverAreaDetailStrategy;                break;            case CHECK_SUPPLIER_AND_CODE_MAPPING:                compareModeStrategy = supplierAndCodeMappingDetailStrategy;                break;            default:;        }        return compareModeStrategy;    }    /**     * 获取 统计信息 策略对象     * */    private CompareModeStrategy getStatisticsCompareModeStrategy(DataCheckProductEnum productEnum){        CompareModeStrategy compareModeStrategy = null;        switch (productEnum){            case CHECK_LANDING_COVER_AREA:                compareModeStrategy = landingCoverAreaStatisticsStrategy;                break;            case CHECK_SUPPLIER_AND_CODE_MAPPING:                compareModeStrategy = supplierAndCodeMappingStatisticsStrategy;                break;            default:;        }        return compareModeStrategy;    }}


使用工厂模式的好处是:

  • 便于bean的管理,所有的bean都在一处创建(或决策)。


  • 条理清晰,便于阅读和维护。


4  “代理模式”

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

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


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


简化后的代码如下:


















































@Servicepublic class CheckLandingCoverAreaService extends AbstractDataCheckProductService {    @Override    public <T> void runDataCheck(List<T> resultList, DataCheckRequestDTO requestDTO){        dataCheckUtils.parallelCheckByFromResCodes(requestDTO,requestDTO.getFromResCodeList(),fromResCode->{            ExpressNetworkQuery query = new ExpressNetworkQuery();            query.setNs(NssEnum.PUB.getId());            query.setStatus(StatusEnum.ENABLE.getId());            query.setGroupNameList(requestDTO.getGroupNameList());            query.setBrandCodeList(requestDTO.getBrandCodeList());            query.setFromResCode(fromResCode);            List<TmsMasterExpressNetworkDO> masterExpressNetworkDOS = tmsMasterExpressNetworkService.queryExpressNetworkTimeList(query);            startCompareWithAnc(resultList,requestDTO,masterExpressNetworkDOS,fromResCode,solutionCodeMap);        });    }}
@Servicepublic class DataCheckUtils {    /**     * 并行处理每个仓     * @param requestDTO 请求参数     * @param fromResCodeList 需要检查的仓列表     * @param checkOperation 具体的业务处理逻辑     * */    public <T> void parallelCheckByFromResCodes(DataCheckRequestDTO requestDTO, List<String> fromResCodeList, Consumer<String> checkOperation){        List<CompletableFuture> futureList = Collections.synchronizedList(new ArrayList<>());        fromResCodeList.forEach(fromResCode->{            CompletableFuture future = CompletableFuture.runAsync(() -> {                try{                    checkOperation.accept(fromResCode);                }catch (Exception e){                    LogPrinter.errorLog("parallelCheckByFromResCodes-error, taskId="+requestDTO.getTaskId(),e);                    recordErrorInfo(requestDTO.getTaskId(),e);                }            }, DATA_CHECK_THREAD_POOL);            futureList.add(future);        });        //等待所有线程结束        futureList.forEach(future->{            try{                future.get();            }catch (Exception e){                LogPrinter.errorLog("parallelCheckByFromResCodes-future-get-error",e);            }        });    }}


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

代理模式的好处是:

  • 统一收口多种不同的业务逻辑,统一做日志和异常处理。


  • 减少重复代码,提高了代码质量。


  • 可维护性较强。


5  其他

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


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

三  思考&感悟

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

  • 将提升代码可读性、可扩展性和可维护性的意识注入到平时的项目中,便于自己,利于他人。如果项目紧急没时间考虑很多,希望之后有时间时能够改善和优化。


  • 工作不仅是为了完成任务,更是提升自己的过程。能力要用将来进行时。


相关文章
|
3天前
|
设计模式 安全 数据库连接
后端开发中的设计模式应用
在软件开发的浩瀚海洋中,设计模式如同灯塔,为后端开发者指引方向。它们不仅仅是代码的模板,更是解决复杂问题的智慧结晶。本文将深入探讨几种常见的设计模式,包括单例模式、工厂模式和观察者模式,并揭示它们在实际应用中如何提升代码的可维护性、扩展性和重用性。通过实例分析,我们将一窥这些模式如何在后端开发中大放异彩,助力构建高效、灵活的软件系统。
|
5天前
|
设计模式 数据库连接 PHP
PHP中的设计模式应用与最佳实践
在本文中,我们将探讨PHP设计模式的应用和最佳实践。通过深入分析,揭示如何在实际项目中有效利用设计模式来优化代码结构、提升系统灵活性和维护性,并分享一些常见设计模式的实际应用案例。无论你是PHP初学者还是经验丰富的开发者,这篇文章都会对你有所帮助。
|
21天前
|
设计模式 存储 前端开发
揭秘.NET架构设计模式:如何构建坚不可摧的系统?掌握这些,让你的项目无懈可击!
【8月更文挑战第28天】在软件开发中,设计模式是解决常见问题的经典方案,助力构建可维护、可扩展的系统。本文探讨了.NET中三种关键架构设计模式:MVC、依赖注入与仓储模式,并提供了示例代码。MVC通过模型、视图和控制器分离关注点;依赖注入则通过外部管理组件依赖提升复用性和可测性;仓储模式则统一数据访问接口,分离数据逻辑与业务逻辑。掌握这些模式有助于开发者优化系统架构,提升软件质量。
33 5
|
23天前
|
设计模式 算法 开发者
深入理解工厂模式与策略模式:设计模式的灵活应用
深入理解工厂模式与策略模式:设计模式的灵活应用
|
1天前
|
设计模式 算法 PHP
PHP中的设计模式:策略模式的应用与实践
在软件开发中,设计模式是解决问题的最佳实践。本文将探讨PHP中的策略模式,通过实际应用案例,展示如何有效地使用策略模式来提高代码的灵活性和可维护性。我们将从基本概念入手,逐步深入到实际编码,最终实现一个具有策略模式的应用。
|
1月前
|
设计模式
设计模式:从理论到实际应用
【8月更文挑战第18天】设计模式是软件工程中解决特定问题的有效方案,提升代码质量并促进团队协作。本文从理论出发,探讨设计模式在实际项目中的应用。设计模式分为创建型、结构型和行为型,遵循如开闭原则等设计原则。通过工厂模式创建不同类型的电子签章,观察者模式实现在状态变更时的通知机制,以及建造者模式灵活组装复杂对象。以虚拟运营商平台为例,采用责任链模式优化审批流程,展示设计模式的实际价值。
|
18天前
|
前端开发 C# 设计模式
“深度剖析WPF开发中的设计模式应用:以MVVM为核心,手把手教你重构代码结构,实现软件工程的最佳实践与高效协作”
【8月更文挑战第31天】设计模式是在软件工程中解决常见问题的成熟方案。在WPF开发中,合理应用如MVC、MVVM及工厂模式等能显著提升代码质量和可维护性。本文通过具体案例,详细解析了这些模式的实际应用,特别是MVVM模式如何通过分离UI逻辑与业务逻辑,实现视图与模型的松耦合,从而优化代码结构并提高开发效率。通过示例代码展示了从模型定义、视图模型管理到视图展示的全过程,帮助读者更好地理解并应用这些模式。
32 0
|
18天前
|
设计模式 安全 数据库连接
|
19天前
|
设计模式 JavaScript 前端开发
从工厂到单例再到策略:Vue.js高效应用JavaScript设计模式
【8月更文挑战第30天】在现代Web开发中,结合使用JavaScript设计模式与框架如Vue.js能显著提升代码质量和项目的可维护性。本文探讨了常见JavaScript设计模式及其在Vue.js中的应用。通过具体示例介绍了工厂模式、单例模式和策略模式的应用场景及其实现方法。例如,工厂模式通过`NavFactory`根据用户角色动态创建不同的导航栏组件;单例模式则通过全局事件总线`eventBus`实现跨组件通信;策略模式用于处理不同的表单验证规则。这些设计模式的应用不仅提高了代码的复用性和灵活性,还增强了Vue应用的整体质量。
14 0
|
27天前
|
设计模式 SQL 缓存
Java编程中的设计模式:单例模式的深入理解与应用
【8月更文挑战第22天】 在Java的世界里,设计模式是构建可维护、可扩展和灵活的软件系统的基石。本文将深入浅出地探讨单例模式这一经典设计模式,揭示其背后的哲学思想,并通过实例演示如何在Java项目中有效运用。无论你是初学者还是资深开发者,这篇文章都将为你打开一扇洞悉软件设计深层逻辑的大门。
26 0