6

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

 3 years ago
source link: https://segmentfault.com/a/1190000040565837
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

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

有段时间没写文章了,最近沉迷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弹窗

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(
    index: 0,
    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 {
  next(dynamic data);
}

///实现init处理器
class _InterceptSingleHandler extends SingleHandler {
  List<InterceptSingle> intercepts;

  int index;

  _InterceptSingleHandler({
    required this.index,
    required this.intercepts,
  });

  @override
  next(dynamic data) {
    if (index >= intercepts.length) {
      return;
    }

    var intercept = intercepts[index];
    var handler =
        _InterceptSingleHandler(index: index + 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(index: 0, intercepts: []);
  _TwiceSubmitHandler _submit = _TwiceSubmitHandler(index: 0, 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 {
  next(dynamic data);
}

///实现init处理器
class _TwiceInitHandler extends TwiceHandler {
  List<InterceptTwice> intercepts;

  int index;

  _TwiceInitHandler({
    required this.index,
    required this.intercepts,
  });

  @override
  next(dynamic data) {
    if (index >= intercepts.length) {
      return;
    }

    var intercept = intercepts[index];
    var handler = _TwiceInitHandler(index: index + 1, intercepts: intercepts);

    intercept.onInit(data, handler);
  }
}

///实现submit处理器
class _TwiceSubmitHandler extends TwiceHandler {
  List<InterceptTwice> intercepts;

  int index;

  _TwiceSubmitHandler({
    required this.index,
    required this.intercepts,
  });

  @override
  next(dynamic data) {
    if (index >= intercepts.length) {
      return;
    }

    var intercept = intercepts[index];
    var handler = _TwiceSubmitHandler(index: index + 1, intercepts: intercepts);

    intercept.onSubmit(data, handler);
  }
}

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

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

img

感谢阅读,下次再会~~

img


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK