一般用于业务开发,不容易有大量使用设计模式的场景。这里总结一下业务开发中比较常用的设计模式。语言当然是Java,基于Spring框架。1责任链模式(ChainofResponsibilityPattern)对数据流和对象进行过滤、验证、处理等一系列操作,这些操作通常是有序的。这种场景在生活中还是很常见的,比如大家经常引用的各种审批流程。再比如面试,需要经过一、二、三、HR面试等等,最后拿到offer。这些都是责任链的场景,责任链模式一般有三个角色:Client:客户端。用于编排处理器,可以在运行时静态或动态生成。类似于单向链表。Handler:接口或抽象类。约定处理函数名称、参数等。ConcreteHandler:具体处理程序。这样的场景其实挺多的,但是真正用到的并不多。这就引出了责任链的几个缺点:灵活性和动态性各有利弊,使得程序不够直观,流程不够清晰。甚至可能存在循环引用(无限循环)。职责链中一般有两种编排:外部编排:特定的处理器,只根据上下文的参数进行处理,并将结果写回上下文。内部没有编排。相对独立(纯)的逻辑处理单元。内部排列:类似于单向链表。在执行过程中,根据Context参数的处理结果,判断是否进行下一步(进入下一个处理器)。与外部布置相比,灵活性略低。1.1实际业务场景在用户留存入金的场景下,我们需要根据线索的渠道号、落地页来源、主题做不同的逻辑处理。但是在得到实际参数之前,我们无法确定使用哪些处理器。一些业务规则(简化):频道号(channel)不能为空一些特殊频道号,推荐人不能为空一些着陆页来源(lpv),首选语言不能为空只有符合条件的线索才能真正进入CRM系统。LeadsLeadsBO@Data@BuilderpublicclassLeadsBO{//通道号privateStringchannel;//推荐人IDprivateLongreferrerId;//科目1:中文2:英文privateIntegersubject;//着陆页版本(LandingPageVersion)privateStringlpv;//偏好语言privateStringpreferenceLanguage;//责任链回写//数据是否有效@Builder.Defaultprivatebooleanvalid=true;//异常信息(错误日志)privateStringerrMsg;}线索过滤器(即Handler处理器)Filter/***leadfilter*/publicinterfaceFilter{voidprocess(LeadsBObo);}4具体处理器/***通道号filter*/@Component("channel")publicclassChannelFilterimplementsFilter{@Overridepublicvoidprocess(LeadsBObo){if(StringUtils.isBlank(bo.getChannel())){bo.setValid(false);bo.setErrMsg("通道为空");}}}/***Referrer过滤器*/@Component("referrer")publicclassReferrerFilterimplementsFilter{privatestaticfinalSetCHANNELS=Set.of("LP-XX-XX-01","LP-XX-XX-02");@Overridepublicvoidprocess(LeadsBObo){finalStringchannel=bo.getChannel();finalLongreferrerId=bo.getReferrerId();if(CHANNELS.contains(channel)&&Objects.isNull(referrerId)){bo.setValid(false);bo.setErrMsg("具体频道号,referrer不能为空");}}}/***登陆页版本号校验*/@Component("lpv")publicclassLpvFilterimplementsFilter{@Overridepublicvoidprocess(LeadsBObo){finalStringlpv=bo.getLpv();if(StringUtils.isNoneBlank(lpv)&&lpv.startsWith("XZ")&&StringUtils.isBlank(bo.getPreferenceLanguage())){bo.setValid(false);bo.setErrMsg("首选语言不能为空");}}}/***主题过滤器**/@Component("subject")publicclassSubjectFilterimplementsFilter{@Overridepublicvoidprocess(LeadsBObo){finalIntegersubject=bo}.getSubject();if(Objects.isNull(subject)||(subject!=1&&subject!=2)){bo.setSubject(1);可以看到我们在定义处理器的时候,并没有在处理器内部动态指定接下来的处理,而是在外部设置处理器的排列方式,更加灵活。借助Spring,给具体的处理加上别名,方便后续使用。接下来定义一个线程拦截服务,这个服务其实是封装了Client的部分逻辑,让调用更简单。仅需要外部传递的参数(LeadsBO)和处理器别名序列(List)。LeadsFilterService/***线索拦截器*/publicinterfaceLeadsFilterService{voidfilter(LeadsBObo,ListfilterChain);}/***线索拦截器*/@Slf4j@ServicepublicclassLeadsFilterServiceImplimplementsLeadsFilterService,ApplicationContextAware{privateApplicationContextappCtx;@Overridepublicvoidfilter(LeadsBObo,ListfilterChain){for(Stringfc:filterChain){finalFilterfilter=appCtx.getBean(fc,Filter.class);过滤过程(bo);if(!bo.isValid()){log.error([线索拦截]数据异常errMsg=>{}",bo.getErrMsg());返回;}}}@OverridepublicvoidsetApplicationContext(ApplicationContextctx)throwsBeansException{appCtx=ctx;}}使用ApplicationContextAware获取Spring容器上下文,方便根据别名获取Bean对象。1.2单机测试@SpringBootTestclassLeadsFilterServiceTest{@AutowiredLeadsFilterService服务;@TestvoidtestChannel(){finalLeadsBObo=LeadsBO.builder().build();service.filter(bo,List.of("channel","subject","lpv","referrer"));assertFalse(bo.isValid());}@TestvoidtestReferrer(){finalLeadsBObo=LeadsBO.builder().channel("LP-XX-XX-01").referrerId(123L).build();service.filter(bo,List.of("channel","subject","lpv","referrer"));assertEquals(1,bo.getSubject());assertTrue(bo.isValid());}}2想想责任链模型能带来什么?高内聚低耦合,将特定的业务逻辑浓缩到特定的处理器上。易于扩展,有新的逻辑,直接继承或实现处理器即可。如果有多个场景需要不同的安排,这些处理器可以被高效地复用。它与策略模式有何不同?策略模式一般只由特定的策略处理,其他策略不会参与。责任链模型是每个处理者都有可能获得处理权。重点在链上。有什么注意事项?避免滥用。但要大胆地用在合适的场景中。一定要避免循环引用。封面图片来源:https://refactoring.guru/desi...echo'5Y6f5Yib5paH56ugOiDmjpjph5Eo5L2g5oCO5LmI5Zad5aW26Iy25ZWKWzkyMzI0NTQ5NzU1NTA4MF0pL+aAneWQpihscGUyMzQp'|base64-d