今天开始记录我在实际工作中, 对设计模式的应用和理解。

本文主要讨论责任链的内容。 简单讲下责任链是什么、项目中的应用场景和实现。

概念

        首先,责任链,顾名思义就是 一个链表。在这个链表中有多个节点(例如节点A,B,C),他们负责不同的工作, 如果节点A处理不了, 则交给节点B; 如果B处理不了,则交给C;知道找到合适的节点处理请求,或遍历完所有节点。

       即责任链模式是指使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

包含两个角色:

      1. 处理者(Handler):接口, 负责规定具体处理用户的请求的方法以及具体处理者,设置后继对象的方法。

      2. 具体处理者(ConcreteHanlder): 接口类的实例,通过调用处理接口规定的方法处理用户请求, 即在街道用户请求后, 处理将调用接口规定的方法,在执行该方法的过程中, 若发现能处理用户请求,就处理有关数据,否则反馈无法处理的消息给用户,然后将用户请求传递给自己的后继对象(即下一个处理节点)。

 

项目中实际应用

1. 责任链在收费规则中的应用

由于我们公司正处于快速发展阶段, 收费规则经常变动。 所以,需要经常调整收费规则代码。(以下所有代码都是调整过,非项目中代码,且部分收费参数都是从数据库中取,为了方便直接写死了) 

首先,这个是前期,我们的收费规则代码(收费规则,1分钟2元,前10分钟免费, 上限是20元。 VIP客户则免费1小时, 1小时1元, 上限5元),

设计模式 -- 责任链模式

代码嵌套多个IF查询,逻辑十分复杂和不清晰。对不了解业务的开发,上手难度较高,且维护修改成本较高。

更糟糕的是,后来,公司有新的需求, 要在原有收费规则的基础上,增加起步价(即在免费时间之后, 2小时内,收费3元。超出部分,按原有收费规则收费:普通1分钟2元,上限是20元,VIP1小时1元, 上限5元)。代码又复杂了好多(很忧伤,当初我上手的时候,看这个规则看了好久),感觉每次增加一个收费规则,就需要重新写一遍收费代码,很苦逼。

设计模式 -- 责任链模式

后期,为了适应公司频繁调整的收费规则。 将收费规则进行拆分,并引入责任链。

1. 两种类型的收费模式, 普通和VIP。 因此,我们使用两种责任链

2. 每种收费模式中, 包括免费, 起步价, 正常收费三种情况, 对应责任链中三个处理者(handler)。

分析完之后, 我们接着分析他们之间的调用关系

1.  收费金额只与时长有关。

2. 收费的时候, 我们先要判断本次使用是否在免费时间内, 是则返回金额0; 不是,继续判断是否在起步价时间内,是则返回起步价金额;不是,则进入正常收费规则, 计算实际收费金额, 并判断是否到达上限,否, 则返回实际收费金额;若超过,则返回上限金额。

那么,就可以整理出一个完整的收费规则的责任链

设计模式 -- 责任链模式

 

为了满足频繁调整的收费要求(有时候可能刚改完代码, 没过多久,领导又要使用之前的收费规则, 真坑。。。。)

我们额外引入了一个工厂类, 通过工厂类,来实例化不同收费模式。

我们声明了两种版本的收费责任链(版本迭代的产物,之前使用的是newDefaultChargeStrategy实例化,后台业务改了,加入起步价,当天最大收费上限等收费规则之后,使用InitiateChargeStrategy的方法来实例化收费规则的责任链)

 

设计模式 -- 责任链模式

InitiateChargeStrategy的责任链如下:

设计模式 -- 责任链模式

通过传入必要参数, 获得调用链,然后调用责任链的getTfee(定义好的获取收费金额的接口)来获取最终的收费金额。

设计模式 -- 责任链模式

2. 统一错误码返回

 我们公司是互联网公司, 用户通过小程序使用公司产品。 且小程序与后台应用各自维护一套错误码标准。 标准格式如下:

flag: 是否服务器异常(true:表示正常返回,无异常; false:表示异常返回,有错误)

ErrCode: 错误码(对应具体的错误信息,例如0000表示成功返回; 0001用户未登录等错误)

obj: 表示返回数据(若flag = true, ErrCode=0000, 返回用户请求的业务数据; 反之, 返回的是错误数据)

同时, 小程序和后台应用同时维护同一套错误码标准。 

鉴于以上描述, 看似合情合理。 但是, 还是存在以下几个问题:

1. 后台应用, 可以捕获业务错误(例如用户未登录), 但是不能捕获所有程序抛出的错误(例如 数据库报错, 代码逻辑错误等), 给前端用户返回不友善的反馈信息。

2. 虽然小程序和后台应用维护了同一份错误码, 但是, 由于开发人员频繁流动,针对什么情况应该返回什么错误码, 新人需要一段时间学习, 且容易使用错误的错误码,导致返回错误码与实际情况不符。

因此,鉴于以上情况。 我们使用责任链的方式, 捕获不同的错误情况。 并根据不同类型的错误,返回不同的错误码。 开发人员只需要在适当的位置,抛出指定的错误即可, 无需花时间去研究错误码与业务场景的关系。

并在AOP拦截中, 对接口调用方法,加try catch, 捕获所有异常, 防止有新的开发人员对异常情况处理,有遗漏的地方。 

同时, 将错误码划分为三种类型: 

1. 业务错误(自定义BizExcepiton,抛出例如用户未登录, session超时等错误)

2. 数据库错误(抛出数据库错误)

3. 逻辑错误(抛出代码逻辑错误, 如RuntimeException等)

并加入责任链, 对错误信息进行处理, 并统一返回错误信息给前端小程序。

 

 

 

 

 

相关文章: