• 如何优雅的在业务中使用设计模式(代码如诗)


    前言

    有段时间没写文章了,最近沉迷Rust,无法自拔,锈儿有毒;这真是门非常有趣的语言,很多地方的设计,真的是满足了我所有的向往。

    当然,这也不是一门简单的语言,提出所有权的概念,引入了极多符号:mut、&mut、ref mut、&、*、as_mut、as_ref。。。让人头秃。。。

    之前看到过一句话,觉得很不错:学习Rust并不会给你带来智商上的优越感,但或许会让你重新爱上编程。

    大家如果阅读过一些开源框架的源码,可能会发现其中数不尽的抽象类,设计模式拈手而来,在功能框架中,可以使用设计模式随心所欲的解耦;在实际的复杂业务中,当然也可以应用合适的设计模式。

    这篇文章,我会结合较为常见的实际业务场景,探讨如何使用合适的设计模式将业务解耦

    • 此处的应用绝不是生搬硬套,是我经过深思熟虑,并将较为复杂的业务进行全面重构后,得出的一套行之有效的思路历程
    • 任何一个设计模式都是一个伟大的经验及其思想总结,千人千面,如果对文章中内容,有不同的意见,希望你能在评论中提出,我们共同探讨,共同进步

    本文章是一篇弱代码类型文章,我会画大量的图片向大家展示,引用设计模式后,会对原有的业务流程,产生什么样的影响。

    前置知识

    这里,需要了解下基础知识,什么是责任链模式和策略模式

    责任链模式,在很多开源框架中都是有所应用,你如果听到啥啥拦截器,基本就是责任链模式,责任链模式的思想很简单,但是有很多种实现方式

    • 最简单的链表实现就和OkHttp的拦截器实现大相径庭
    • OkHttp的拦截器实现和Dio拦截器实现结构相同,但遍历方式不一样
    • 很多骚操作:我喜欢OkHttp的实现方式,喜欢dio的Api设计,结尾会给出一个结合这俩者思想的通用拦截器

    策略模式,或是天生适合业务,同一模块不同类型业务,如果行为相同,或许就可以考虑使用策略模式去解耦了

    责任链模式

    这边用Dart写一个简单的拦截器,dart和java非常像

    • 为了减少语言差异,我就不使用箭头语法了
    • 下划线表示私有

    用啥语言不重要,这边只是用代码简单演示下思想

    此处实现就用链表了;如果,使用数组的形式,需要多写很多逻辑,数组的优化写法在结尾给出,此处暂且不表

    结构

    • 责任链的结构,通常有俩种结构
      • 链表结构:链表构建责任链,十分便捷的就能和下一节点建立联系
      • 数组结构:数组,用通用的List即可,方便增删,不固定长度(别费劲的用固定长度Array了,例如:int[]、String[])

    责任链结构

    • 实现一个链表实体很简单
    abstract class InterceptChain<T> {
      InterceptChain? next;
    
      void intercept(T data) {
        next?.intercept(data);
      }
    }
    

    实现

    • 拦截器实现
    /// 该拦截器以最简单的链表实现
    abstract class InterceptChain<T> {
      InterceptChain? next;
    
      void intercept(T data) {
        next?.intercept(data);
      }
    }
    
    class InterceptChainHandler<T> {
      InterceptChain? _interceptFirst;
    
      void add(InterceptChain interceptChain) {
        if (_interceptFirst == null) {
          _interceptFirst = interceptChain;
          return;
        }
    
        var node = _interceptFirst!;
        while (true) {
          if (node.next == null) {
            node.next = interceptChain;
            break;
          }
          node = node.next!;
        }
      }
    
      void intercept(T data) {
        _interceptFirst?.intercept(data);
      }
    }
    
    • 使用
      • 调整add顺序,就调整了对应逻辑的节点,在整个责任链中的顺序
      • 去掉intercept重写方法中的super.intercept(data),就能实现拦截后续节点逻辑
    void main() {
      var intercepts = InterceptChainHandler<String>();
      intercepts.add(OneIntercept());
      intercepts.add(TwoIntercept());
      intercepts.intercept("测试拦截器");
    }
    
    class OneIntercept extends InterceptChain<String> {
      @override
      void intercept(String data) {
        data = "$data:OneIntercept";
        print(data);
        super.intercept(data);
      }
    }
    
    class TwoIntercept extends InterceptChain<String> {
      @override
      void intercept(String data) {
        data = "$data:TwoIntercept";
        print(data);
        super.intercept(data);
      }
    }
    
    • 打印结果
    测试拦截器:OneIntercept
    测试拦截器:OneIntercept:TwoIntercept
    

    策略模式

    结构

    • 策略模式最重要的:应该就是对抽象类的设计,对行为的抽象

    策略模式应用

    实现

    • 定义抽象类,抽象行为
    /// 结合适配器模式的接口适配:抽象必须实现行为,和可选实现行为
    abstract class BusinessAction {
      ///创建相应资源:该行为必须实现
      void create();
    
      ///可选实现
      void dealIO() {}
    
      ///可选实现
      void dealNet() {}
    
      ///可选实现
      void dealSystem() {}
    
      ///释放资源:该行为必须实现
      void dispose();
    }
    
    • 实现策略类
    //Net策略
    class NetStrategy extends BusinessAction {
      @override
      void create() {
        print("创建Net资源");
      }
    
      @override
      void dealNet() {
        print("处理Net逻辑");
      }
    
      @override
      void dispose() {
        print("释放Net资源");
      }
    }
    
    ///IO策略
    class IOStrategy extends BusinessAction {
      @override
      void create() {
        print("创建IO资源");
      }
    
      @override
      void dealIO() {
        print("处理IO逻辑");
      }
    
      @override
      void dispose() {
        print("释放IO资源");
      }
    }
    
    • 使用
    void main() {
      var type = 1;
      BusinessAction strategy;
    
      //不同业务使用不同策略
      if (type == 0) {
        strategy = NetStrategy();
      } else {
        strategy = IOStrategy();
      }
    
      //开始创建资源
      strategy.create();
      //......... 省略N多逻辑(其中某些场景,会有用到Net业务,和上面type是关联的)
      //IO业务:开始处理业务
      strategy.dealIO();
      //......... 省略N多逻辑
      //释放资源
      strategy.dispose();
    }
    
    • 结果
    创建IO资源
    处理IO逻辑
    释放IO资源
    

    适合的业务场景

    这边举一些适合上述设计模式的业务场景,这些场景是真实存在的!

    这些真实的业务,使用设计模式解耦和纯靠if else怼,完全是俩种体验!

    代码如诗,这并不是一句玩笑话。

    连环弹窗业务

    业务描述

    连环弹窗夺命call来袭。。。

    • A弹窗弹出:有确定和取消按钮
      • 确定按钮:B弹窗弹出(有查看详情和取消按钮)
        • 查看详情按钮:C弹窗弹出(有同意和拒绝按钮)
          • 同意按钮:D弹窗弹出(有查看和下一步按钮)
            • 查看按钮:E弹窗弹出(只有下一步按钮)
              • 下一步按钮:F弹窗弹出(结束)
            • 下一步按钮:F弹窗弹出(结束)
          • 拒绝按钮:流程结束
        • 取消按钮:流程结束
      • 取消按钮:流程结束

    好家伙,套娃真是无所不在,真不是我们代码套娃,实在是业务套娃,手动滑稽.png

    img

    • 图示弹窗业务

    连环弹窗业务1

    直接开搞

    看到这个业务,大家会去怎么做呢?

    • 有人可能会想,这么简单的业务还需要想吗?直接写啊!
      • A:在确定回调里面,跳转B弹窗
      • B:查看详情按钮跳转C弹窗
      • 。。。
    • 好一通套后,终于写完了

    产品来了,加需求

    B和C弹窗之间要加个预览G弹窗,点击B的查看详情按钮,跳转预览G弹窗;预览G弹窗只有一个确定按钮,点击后跳转C弹窗

    img

    • 你心里可能要想了,这特么不是坑爹?
      • 业务本来就超吉尔套,我B弹窗里面写的跳转代码要改,传参要改,而且还要加弹窗!
    • 先要去找产品撕比,撕完后
      • 然后继续在屎山上,小心翼翼的再拉了坨shit
      • 这座克苏鲁山初成规模

    连环弹窗业务2

    产品又来了,第一稿需求不合理,需要调整需求

    交换C和D弹窗位置,逻辑调整:D弹窗点击下一步的时候,需要加一个校验请求,通过后跳转到C弹窗,点击查看按钮跳转弹窗F

    img

    • 你眉头一皱,发现事情没有表面这么简单
      • 由于初期图简单,几乎都写在一个文件里,眼花缭乱弹窗回调太多,而且弹窗样式也不一样
      • 现在改整个流程,导致你整个人脑子嗡嗡响
    • 心中怒气翻涌,找到产品说

    img

    • 回来,坐在椅子上,心里想:
      • 老夫写的代码天衣无缝,这什么几把需求
      • 可恶,这次测试,起码要给我多提十几个BUG

    image-20210822215435299

    • 克苏鲁山开始狰狞

    连环弹窗业务3

    产品飘来,加改需求:如此,如此,,,这般,这般,,,

    • 你....

    img

    产品:改下,,,然后,扔给你几十页的PRD

    你看了看这改了几十版的克苏鲁山,这几十个弹窗逻辑居然都写在一个文件里,快一万行的代码。。。

    • 心里不禁想:
      • 本帅比写的代码果然牛批,或许这就是艺术!艺术总是曲高和寡,难被人理解!而我的代码更牛批,连我自己都看不懂了!
      • 这代码行数!这代码结构!不得拍个照留念下,传给以后的孩子当传家宝供着!
    • 心里不禁嘚瑟:
      • 这块业务,除了我,还有谁敢动,成为头儿的心腹,指日可待!

    16c3-ikhvemy5945899

    • 但,转念深思后:事了拂衣去,深藏功与名

    img

    重构

    随着业务的逐渐复杂,最初的设计缺点会逐渐暴露;重构有缺陷的代码流程,变得势在必行,这会极大的降低维护成本

    如果心中对责任链模式有一些概念的话,会发现上面的业务,极其适合责任链模式!

    对上面的业务进行分析,可以明确一些事

    • 这个业务是一个链式的,有着明确的方向性:单向,从头到尾指向
    • 业务拆分开,可以将一个弹窗作为单颗粒度,一个弹窗作为节点
    • 上级的业务节点可以对下级节点拦截(点击取消,拒绝按钮,不再进行后续业务)

    重构上面的代码,只要明确思想和流程就行了

    第一稿业务

    • 业务流程

    连环弹窗业务1

    • 责任链

    责任链业务1

    • 代码:简写
    void main() {
      var intercepts = InterceptChainHandler<String>();
      intercepts.add(AIntercept());
      intercepts.add(BIntercept());
      intercepts.add(CIntercept());
      intercepts.add(DIntercept());
      intercepts.add(EIntercept());
      intercepts.add(FIntercept());
      intercepts.intercept("测试拦截器");
    }
    

    第二稿业务

    • 业务流程

    连环弹窗业务2

    • 责任链

    责任链业务2

    • 代码:简写
    void main() {
      var intercepts = InterceptChainHandler<String>();
      intercepts.add(AIntercept());
      intercepts.add(BIntercept());
      intercepts.add(GIntercept());
      intercepts.add(CIntercept());
      intercepts.add(DIntercept());
      intercepts.add(EIntercept());
      intercepts.add(FIntercept());
      intercepts.intercept("测试拦截器");
    }
    

    第三稿业务

    • 业务流程

    连环弹窗业务3

    • 责任链

    责任链业务3

    • 代码:简写
    void main() {
      var intercepts = InterceptChainHandler<String>();
      intercepts.add(AIntercept());
      intercepts.add(BIntercept());
      intercepts.add(GIntercept());
      intercepts.add(DIntercept());
      intercepts.add(CIntercept());
      intercepts.add(EIntercept());
      intercepts.add(FIntercept());
      intercepts.intercept("测试拦截器");
    }
    

    总结

    经过责任链模式重构后,业务节点被明确的区分开,整个流程从代码上看,都相当的清楚,维护将变的异常轻松;或许,此时能感受到一些,编程的乐趣了

    img

    花样弹窗业务

    业务描述

    来描述一个新的业务:这个业务场景真实存在某办公软件

    • 进入APP首页后,和后台建立一个长连接
    • 后台某些工单处理后,会通知APP处理,此时app会弹出处理工单的弹窗(app顶部)
    • 弹窗类型很多:工单处理弹窗,流程审批弹窗,邀请类型弹窗,查看工单详情弹窗,提交信息弹窗。。。
    • 弹窗弹出类型,是根据后台给的Type进行判断:从而弹出不同类型弹窗、点击其按钮,跳转不同业务,传递不同参数。

    花样弹窗业务

    分析

    确定设计

    这个业务,是一种渐变性的引导你搭建克苏鲁代码山

    • 在前期开发的时候,一般只有俩三种类型弹窗,前期十分好做;根本不用考虑如何设计,抬手一行代码,反手一行代码,就能搞定
    • 但是后来整个业务会渐渐的鬼畜,不同类型会慢慢加到几十种之多!!!

    首先这个业务,使用责任链模式,肯定是不合适的,因为弹窗之间的耦合性很低,并没有什么明确的上下游关系

    但是,这个业务使用策略模式非常的合适!

    • type明确:不同类型弹出不同弹窗,按钮执行不同逻辑
    • 抽象行为明确:一个按钮就是一种行为,不同行为的实现逻辑大相径庭

    抽象行为

    多样弹窗的行为抽象,对应其按钮就行了

    确定、取消、同意、拒绝、查看详情、我知道了、提交

    直接画图来表示吧

    花样弹窗业务-抽象行为

    实现

    来看下简要的代码实现,代码不重要,重要的是思想,这边简要的看下代码实现流程

    • 抽象基类
    /// 默认实现抛异常,可提醒未实现方法被误用
    abstract class DialogAction {
      ///确定
      void onConfirm() {
        throw 'DialogAction:not implement onConfirm()';
      }
    
      ///取消
      void onCancel() {
        throw 'DialogAction:not implement onCancel()';
      }
    
      ///同意
      void onAgree() {
        throw 'DialogAction:not implement onAgree()';
      }
    
      ///拒绝
      void onRefuse() {
        throw 'DialogAction:not implement onRefuse()';
      }
    
      ///查看详情
      void onDetail() {
        throw 'DialogAction:not implement onDetail()';
      }
    
      ///我知道了
      void onKnow() {
        throw 'DialogAction:not implement onKnow()';
      }
    
      ///提交
      void onSubmit() {
        throw 'DialogAction:not implement onSubmit()';
      }
    }
    
    • 实现逻辑类
    class OneStrategy extends DialogAction {
      @override
      void onConfirm() {
        print("确定");
      }
    
      @override
      void onCancel() {
        print("取消");
      }
    }
    
    class TwoStrategy extends DialogAction{
      @override
      void onAgree() {
        print("同意");
      }
      
      @override
      void onRefuse() {
        print("拒绝");
      }
    }
    
    //........省略其他实现
    
    • 使用
    void main() {
      //根据接口获取
      var type = 1;
      DialogAction strategy;
      switch (type) {
        case 0:
          strategy = DefaultStrategy();
          break;
        case 1:
          strategy = OneStrategy();
          break;
        case 2:
          strategy = TwoStrategy();
          break;
        case 3:
          strategy = ThreeStrategy();
          break;
        case 4:
          strategy = FourStrategy();
          break;
        case 5:
          strategy = FiveStrategy();
          break;
        default:
          strategy = DefaultStrategy();
          break;
      }
    
      //聚合弹窗按钮触发事件(不同弹窗的确定按钮,皆可聚合为一个onConfirm事件,其它同理)
      BusinessDialog(
        //通过传入的type,显示对应类型的弹窗
        type: type,
        //确定按钮
        onConfirm: () {
          strategy.onConfirm();
        },
        //取消按钮
        onCancel: () {
          strategy.onCancel();
        },
        //同意按钮
        onAgree: () {
          strategy.onAgree();
        },
        //拒绝按钮
        onRefuse: () {
          strategy.onRefuse();
        },
        //查看详情按钮
        onDetail: () {
          strategy.onDetail();
        },
        //我知道了按钮
        onKnow: () {
          strategy.onKnow();
        },
        //提交按钮
        onSubmit: () {
          strategy.onSubmit();
        },
      );
    }
    
    • 图示

    花样弹窗业务-业务流程

    一个复杂业务场景的演变

    我们看下,一个简单的提交业务流,怎么逐渐变的狰狞

    我会逐渐给出一个合适的解决方案,如果大家有更好的想法,务必在评论区告诉鄙人

    业务描述:我们的车子因不可抗原因坏了,要去维修厂修车,工作人员开始登记这个损坏车辆。。。

    业务的演变

    第一稿

    初始业务

    登记一个维修车辆的流程,实际上还是满麻烦的

    • 登记一个新车,需要将车辆详细信息登记清楚:车牌、车架、车型号、车辆类型、进出场时间、油量、里程。。。
    • 还需要登记一下用户信息:姓名、手机号、是否隶属公司。。。
    • 登记车损程度:车顶、车底、方向盘、玻璃、离合器、刹车。。。
    • 车内物品:车座皮套、工具。。。
    • 以及其他我没想到的。。。
    • 最后:提交所有登记好的信息

    第一稿,业务流程十分清晰,细节复杂,但是做起来不难

    车辆登记-第一稿

    第二稿(实际是多稿聚合):增加下述几个流程

    外部登记:外部登记了一个维修车辆部分信息(后台,微信小程序,H5等等),需要在app上完善信息,提交接口不同(必带车牌号)

    快捷洗车:洗车业务极其常见,快捷生成对应信息,提交接口不同

    预约订单登记:预约好了车辆一部分一些信息,可快捷登记,提交接口不同(必带车牌号)

    因为登记维修车辆流程,登记车辆信息流程极其细致繁琐,我们决定复用登记新车模块

    • 因为此处逻辑大多涉及开头和结尾,中间登记车辆信息操作几乎未改动,复用想法是可行的
    • 如果增加车辆登记项,新的三个流程也必须提交这些信息;所以,复用势在必行

    因为这一稿需求,业务也变得愈加复杂

    车辆登记-第二稿

    第三稿

    现在要针对不同的车辆类型,做不同的处理;车类型分:个人车,集团车

    不同类型的登记,在提交的时候,需要校验不同的信息;校验不通过,需要提示用户,并且不能进行提交流程

    提交后,需要处理下通用业务,然后跳转到某个页面

    第三稿的描述不多,但是,大大的增加了复杂度

    • 尤其是不同类型校验过程还不同,还能中断后续提交流程
    • 提交流程后,还需要跳转通用页面

    车辆登记-第三稿

    开发探讨

    第一稿

    • 业务流程

    车辆登记-第一稿

    • 开发

    正常流程开发、、、

    第二稿

    • 业务流程

    车辆登记-第二稿

    • 思考

    对于第二稿业务,可以好好考虑下,怎么去设计?

    开头和结尾需要单独写判断,去处理不同流程的业务,这至少要写俩个大的判断模块,接受数据的入口模块可能还要写判断

    这样就非常适合策略模式去做了

    开头根据执行的流程,选择相应的策略对象,后续将逻辑块替换抽象的策略方法就OK了,大致流程如下

    车辆登记-第二稿(策略模式)

    第三稿

    业务流程

    车辆登记-第三稿

    探讨

    • 第三稿的需求,实际上,已经比较复杂了

      • 整个流程中掺杂着不同业务流程处理,不同流程逻辑又拥有阻断下游机制(绿色模块)
      • 下游逻辑又会合流(结尾)的多种变换
    • 在这一稿的需求

      • 使用策略模式肯定是可以的
      • 阻断那块(绿色模块)需要单独处理下:抽象方法应该拥有返回值,外层根据返回值,判断是否进行后续流程
      • 但!这!也太不优雅了!
    • 思考上面业务一些特性

      • 拦截下游机制
      • 上游到下游、方向明确
      • 随时可能插入新的业务流程。。。

    可以用责任链模式!但,需要做一些小改动!这地方,我们可以将频繁变动的模块用责任链模式全都隔离出来

    • 看下,使用责任链模式改造后流程图

    车辆登记-第三稿(责任链模式)

    浏览上述流程图可发现,本来是极度杂乱糅合的业务,可以被设计相对更加平行的结构

    • 对于上述流程,可以进一步分析,并进一步简化:对整体业务分析,我们需要去关注其变或不变的部分

      • 不变:整体业务变动很小的是,登记信息流程(主体逻辑这块),此处的相关变动是很小的,对所有流程也是共用的部分
      • 变:可以发现,开头和结尾是变动更加频繁的部分,我们可以对此处逻辑进行整体的抽象
    • 抽象多变的开头和结尾

    车辆登记-第三稿(责任链模式——简化)

    • 所以我们抽象拦截类,可以做一些调整
    abstract class InterceptChainTwice<T> {
      InterceptChainTwice? next;
    
      void onInit(T data) {
        next?.onInit(data);
      }
    
      void onSubmit(T data) {
        next?.onSubmit(data);
      }
    }
    

    来看下简要的代码实现,代码不重要,主要看看实现流程和思想

    • 抽象拦截器
    abstract class InterceptChainTwice<T> {
      InterceptChainTwice? next;
    
      void onInit(T data) {
        next?.onInit(data);
      }
    
      void onSubmit(T data) {
        next?.onSubmit(data);
      }
    }
    
    class InterceptChainTwiceHandler<T> {
      InterceptChainTwice? _interceptFirst;
    
      void add(InterceptChainTwice interceptChain) {
        if (_interceptFirst == null) {
          _interceptFirst = interceptChain;
          return;
        }
    
        var node = _interceptFirst!;
        while (true) {
          if (node.next == null) {
            node.next = interceptChain;
            break;
          }
          node = node.next!;
        }
      }
    
      void onInit(T data) {
        _interceptFirst?.onInit(data);
      }
    
      void onSubmit(T data) {
        _interceptFirst?.onSubmit(data);
      }
    }
    
    • 实现拦截器
    /// 开头通用拦截器
    class CommonIntercept extends InterceptChainTwice<String> {
      @override
      void onInit(String data) {
        //如果有车牌,请求接口,获取数据
        //.................
        //填充页面
        super.onInit(data);
      }
    }
    
    /// 登记新车拦截器
    class RegisterNewIntercept extends InterceptChainTwice<String> {
      @override
      void onInit(String data) {
        //处理开头针对登记新车的单独逻辑
        super.onInit(data);
      }
    
      @override
      void onSubmit(String data) {
        var isPass = false;
        //如果校验不过,拦截下游逻辑
        if (!isPass) {
          return;
        }
        // ......
        super.onSubmit(data);
      }
    }
    
    /// 省略其他实现
    
    • 使用
    void main() {
      var type = 0;
      var intercepts = InterceptChainTwiceHandler();
    
      intercepts.add(CommonIntercept());
      intercepts.add(CarTypeDealIntercept());
      if (type == 0) {
        //登记新车
        intercepts.add(RegisterNewCarIntercept());
      } else if (type == 1) {
        //外部登记
        intercepts.add(OutRegisterIntercept());
      } else if (type == 2) {
        //快捷洗车
        intercepts.add(FastWashIntercept());
      } else {
        //预约订单登记
        intercepts.add(OrderRegisterIntercept());
      }
      intercepts.add(TailIntercept());
    
      //业务开始
      intercepts.onInit("传入数据源");
    
      //开始处理N多逻辑
      //............................................................
      //经历了N多逻辑
    
      //提交按钮触发事件
      SubmitBtn(
        //提交按钮
        onSubmit: () {
          intercepts.onSubmit("传入提交数据");
        },
      );
    }
    

    总结

    关于代码部分,关键的代码,我都写出来,用心看看,肯定能明白我写的意思

    也不用找我要完整代码了,这些业务demo代码写完后,就删了

    本栏目这个业务,实际上是非常常见的的一个业务,一个提交流程与很多其它的流程耦合,整个业务就会慢慢的变的鬼畜,充满各种判断,很容易让人陷入泥泞,或许,此时可以对已有业务进行思考,如何进行合理的优化

    该业务的演变历程,和开发改造是本人的一次思路历程,如大家有更好的思路,还请不吝赐教。

    通用拦截器

    我结合OkHttp的思想和Dio的API,封装了俩个通用拦截器,这边贴下代码,如果哪里有什么不足,请及时告知本人

    说明下:这是Dart版本的

    抽象单方法

    ///一层通用拦截器,T的类型必须一致
    abstract class InterceptSingle<T> {
      void intercept(T data, SingleHandler handler) => handler.next(data);
    }
    
    ///添加拦截器,触发拦截器方法入口
    class InterceptSingleHandler<T> {
      _InterceptSingleHandler _handler = _InterceptSingleHandler(intercepts: []);
    
      void add(InterceptSingle intercept) {
        //一种类型的拦截器只能添加一次
        for (var item in _handler.intercepts) {
          if (item.runtimeType == intercept.runtimeType) {
            return;
          }
        }
    
        _handler.intercepts.add(intercept);
      }
    
      void delete(InterceptSingle intercept) {
        _handler.intercepts.remove(intercept);
      }
    
      void intercept(T data) {
        _handler.next(data);
      }
    }
    
    ///------------实现不同处理器 参照 dio api设计 和 OkHttp实现思想---------------
    abstract class SingleHandler {
      /// span: 设置该参数,可控跨越多级节点
      /// 默认0,则不跨越节点(遍历所有节点)
      next(dynamic data, {int span = 0});
    }
    
    ///实现init处理器
    class _InterceptSingleHandler extends SingleHandler {
      List<InterceptSingle> intercepts;
    
      int index;
    
      _InterceptSingleHandler({
        this.index = 0,
        required this.intercepts,
      });
    
      @override
      next(dynamic data, {int span = 0}) {
        if ((index + span) >= intercepts.length) return;
    
        var intercept = intercepts[index + span];
        var handler = _InterceptSingleHandler(
          index: index + (span + 1),
          intercepts: intercepts,
        );
    
        intercept.intercept(data, handler);
      }
    }
    

    抽象双方法

    ///俩层通用拦截器,T的类型必须一致
    abstract class InterceptTwice<T> {
      void onInit(T data, TwiceHandler handler) => handler.next(data);
    
      void onSubmit(T data, TwiceHandler handler) => handler.next(data);
    }
    
    ///添加拦截器,触发拦截器方法入口
    class InterceptTwiceHandler<T> {
      _TwiceInitHandler _init = _TwiceInitHandler(intercepts: []);
      _TwiceSubmitHandler _submit = _TwiceSubmitHandler(intercepts: []);
    
      void add(InterceptTwice intercept) {
        //一种类型的拦截器只能添加一次
        for (var item in _init.intercepts) {
          if (item.runtimeType == intercept.runtimeType) {
            return;
          }
        }
    
        _init.intercepts.add(intercept);
        _submit.intercepts.add(intercept);
      }
    
      void delete(InterceptTwice intercept) {
        _init.intercepts.remove(intercept);
        _submit.intercepts.remove(intercept);
      }
    
      void onInit(T data) {
        _init.next(data);
      }
    
      void onSubmit(T data) {
        _submit.next(data);
      }
    }
    
    ///------------实现不同处理器 参照 dio api设计 和 OkHttp实现思想---------------
    abstract class TwiceHandler {
      /// span: 设置该参数,可控跨越多级节点
      /// 默认0,则不跨越节点(遍历所有节点)
      next(dynamic data, {int span = 0});
    }
    
    ///实现init处理器
    class _TwiceInitHandler extends TwiceHandler {
      List<InterceptTwice> intercepts;
    
      int index;
    
      _TwiceInitHandler({
        this.index = 0,
        required this.intercepts,
      });
    
      @override
      next(dynamic data, {int span = 0}) {
        if ((index + span) >= intercepts.length) return;
    
        var intercept = intercepts[index + span];
        var handler = _TwiceInitHandler(
          index: index + (span + 1),
          intercepts: intercepts,
        );
    
        intercept.onInit(data, handler);
      }
    }
    
    ///实现submit处理器
    class _TwiceSubmitHandler extends TwiceHandler {
      List<InterceptTwice> intercepts;
    
      int index;
    
      _TwiceSubmitHandler({
        this.index = 0,
        required this.intercepts,
      });
    
      @override
      next(dynamic data, {int span = 0}) {
        if ((index + span) >= intercepts.length) {
          return;
        }
    
        var intercept = intercepts[index + span];
        var handler = _TwiceSubmitHandler(
          index: index + (span + 1),
          intercepts: intercepts,
        );
    
        intercept.onSubmit(data, handler);
      }
    }
    

    最后

    第一次,写这种结合业务的文章

    如有收获,还请点个赞,让我感受一下,各位是否读有所获~~

    img

    感谢阅读,下次再会~~

    img

  • 相关阅读:
    java web(七)Cookie的简单使用
    java web(六)多个请求对应一个Servlet
    <十六>JDBC_使用 DBUtils 编写通用的DAO
    <十五>JDBC_使用 DBUtils 进行更新、查询操作
    附加作业
    第五周作业
    第八周作业
    第七周作业
    第一周作业
    第六周作业
  • 原文地址:https://www.cnblogs.com/xdd666/p/15180316.html
Copyright © 2020-2023  润新知