链式调用 | Go设计模式实战
嗯,Go设计模式实战系列,一个设计模式业务真实使用的golang系列。
http://tigerb.cn/
前言
本系列主要分享,如何在我们的真实业务场景中使用设计模式。
本系列文章主要采用如下结构:
- 什么是「XX设计模式」?
- 什么真实业务场景可以使用「XX设计模式」?
- 怎么用「XX设计模式」?
本文主要介绍「责任链模式」如何在真实业务场景中使用。
什么是「责任链模式」?
首先把一系列业务按职责划分成不同的对象,接着把这一系列对象构成一个链,然后在这一系列对象中传递请求对象,直到被处理为止。
我们从概念中可以看出责任链模式有如下明显的优势:
但是有一点直到被处理为止
,代表最终只会被一个实际的业务对象执行了实际的业务逻辑,明显适用的场景并不多。但是除此之外,上面的那两点优势还是让人很心动,所以,为了适用于目前所接触的绝大多数业务场景,把概念进行了简单的调整,如下:
首先把一系列业务按职责划分成不同的对象,接着把这一系列对象构成一个链,直到“链路结束”为止。(结束:异常结束,或链路执行完毕结束)
简单的直到“链路结束”为止
转换可以让我们把责任链模式适用于任何复杂的业务场景。
以下是责任链模式的具体优势:
- 直观:一眼可观的业务调用过程
- 无限扩展:可无限扩展的业务逻辑
- 高度封装:复杂业务代码依然高度封装
- 极易被修改:复杂业务代码下修改代码只需要专注对应的业务类(结构体)文件即可,以及极易被调整的业务执行顺序
什么真实业务场景可以用「责任链模式(改)」?
满足如下要求的场景:
业务极度复杂的所有场景
任何杂乱无章的业务代码,都可以使用责任链模式(改)去重构、设计。
我们有哪些真实业务场景可以用「责任链模式(改)」呢?
比如电商系统的下单接口,随着业务发展不断的发展,该接口会充斥着各种各样的业务逻辑。
怎么用「责任链模式(改)」?
关于怎么用,完全可以生搬硬套我总结的使用设计模式的四个步骤:
业务梳理
步骤 |
逻辑 |
1 |
参数校验 |
2 |
获取地址信息 |
3 |
地址信息校验 |
4 |
获取购物车数据 |
5 |
获取商品库存信息 |
6 |
商品库存校验 |
7 |
获取优惠信息 |
8 |
获取运费信息 |
9 |
使用优惠信息 |
10 |
扣库存 |
11 |
清理购物车 |
12 |
写订单表 |
13 |
写订单商品表 |
14 |
写订单优惠信息表 |
XX |
以及未来会增加的逻辑... |
业务的不断发展变化的:
比如增加的新的业务,订金预售:
- 在
4|获取购物车数据
后,需要校验商品参见订金预售活动的有效性等逻辑。
- 等等逻辑
注:流程不一定完全准确
业务流程图
我们通过梳理的文本业务流程得到了如下的业务流程图:
代码建模
责任链模式主要类主要包含如下特性:
- 成员属性
nextHandler
: 下一个等待被调用的对象实例 -> 稳定不变的
- 成员方法
SetNext
: 把下一个对象的实例绑定到当前对象的nextHandler
属性上 -> 稳定不变的
Do
: 当前对象业务逻辑入口 -> 变化的
Run
: 调用当前对象的Do
,nextHandler
不为空则调用nextHandler.Do
-> 稳定不变的
套用到下单接口伪代码实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| 一个父类(抽象类):
- 成员属性 + `nextHandler`: 下一个等待被调用的对象实例 - 成员方法 + 实体方法`SetNext`: 实现把下一个对象的实例绑定到当前对象的`nextHandler`属性上 + 抽象方法`Do`: 当前对象业务逻辑入口 + 实体方法`Run`: 实现调用当前对象的`Do`,`nextHandler`不为空则调用`nextHandler.Do`
子类一(参数校验) - 继承抽象类父类 - 实现抽象方法`Do`:具体的参数校验逻辑
子类二(获取地址信息) - 继承抽象类父类 - 实现抽象方法`Do`:具体获取地址信息的逻辑
子类三(获取购物车数据) - 继承抽象类父类 - 实现抽象方法`Do`:具体获取购物车数据的逻辑
......略
子类X(以及未来会增加的逻辑) - 继承抽象类父类 - 实现抽象方法`Do`:以及未来会增加的逻辑
|
但是,golang里没有的继承的概念,要复用成员属性nextHandler
、成员方法SetNext
、成员方法Run
怎么办呢?我们使用合成复用
的特性变相达到“继承复用”的目的,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| 一个接口(interface):
- 抽象方法`SetNext`: 待实现把下一个对象的实例绑定到当前对象的`nextHandler`属性上 - 抽象方法`Do`: 待实现当前对象业务逻辑入口 - 抽象方法`Run`: 待实现调用当前对象的`Do`,`nextHandler`不为空则调用`nextHandler.Do`
一个基础结构体:
- 成员属性 + `nextHandler`: 下一个等待被调用的对象实例 - 成员方法 + 实体方法`SetNext`: 实现把下一个对象的实例绑定到当前对象的`nextHandler`属性上 + 实体方法`Run`: 实现调用当前对象的`Do`,`nextHandler`不为空则调用`nextHandler.Do`
子类一(参数校验) - 合成复用基础结构体 - 实现抽象方法`Do`:具体的参数校验逻辑
子类二(获取地址信息) - 合成复用基础结构体 - 实现抽象方法`Do`:具体获取地址信息的逻辑
子类三(获取购物车数据) - 合成复用基础结构体 - 实现抽象方法`Do`:具体获取购物车数据的逻辑
......略
子类X(以及未来会增加的逻辑) - 合成复用基础结构体 - 实现抽象方法`Do`:以及未来会增加的逻辑
|
同时得到了我们的UML图:
代码demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
| package main
import ( "fmt" "runtime" )
type Context struct { }
type Handler interface { Do(c *Context) error SetNext(h Handler) Handler Run(c *Context) error }
type Next struct { nextHandler Handler }
func (n *Next) SetNext(h Handler) Handler { n.nextHandler = h return h }
func (n *Next) Run(c *Context) (err error) { if n.nextHandler != nil { if err = (n.nextHandler).Do(c); err != nil { return } return (n.nextHandler).Run(c) } return }
type NullHandler struct { Next }
func (h *NullHandler) Do(c *Context) (err error) { return }
type ArgumentsHandler struct { Next }
func (h *ArgumentsHandler) Do(c *Context) (err error) { fmt.Println(runFuncName(), "校验参数成功...") return }
type AddressInfoHandler struct { Next }
func (h *AddressInfoHandler) Do(c *Context) (err error) { fmt.Println(runFuncName(), "获取地址信息...") fmt.Println(runFuncName(), "地址信息校验...") return }
type CartInfoHandler struct { Next }
func (h *CartInfoHandler) Do(c *Context) (err error) { fmt.Println(runFuncName(), "获取购物车数据...") return }
type StockInfoHandler struct { Next }
func (h *StockInfoHandler) Do(c *Context) (err error) { fmt.Println(runFuncName(), "获取商品库存信息...") fmt.Println(runFuncName(), "商品库存校验...") return }
type PromotionInfoHandler struct { Next }
func (h *PromotionInfoHandler) Do(c *Context) (err error) { fmt.Println(runFuncName(), "获取优惠信息...") return }
type ShipmentInfoHandler struct { Next }
func (h *ShipmentInfoHandler) Do(c *Context) (err error) { fmt.Println(runFuncName(), "获取运费信息...") return }
type PromotionUseHandler struct { Next }
func (h *PromotionUseHandler) Do(c *Context) (err error) { fmt.Println(runFuncName(), "使用优惠信息...") return }
type StockSubtractHandler struct { Next }
func (h *StockSubtractHandler) Do(c *Context) (err error) { fmt.Println(runFuncName(), "扣库存...") return }
type CartDelHandler struct { Next }
func (h *CartDelHandler) Do(c *Context) (err error) { fmt.Println(runFuncName(), "清理购物车...") return }
type DBTableOrderHandler struct { Next }
func (h *DBTableOrderHandler) Do(c *Context) (err error) { fmt.Println(runFuncName(), "写订单表...") return }
type DBTableOrderSkusHandler struct { Next }
func (h *DBTableOrderSkusHandler) Do(c *Context) (err error) { fmt.Println(runFuncName(), "写订单商品表...") return }
type DBTableOrderPromotionsHandler struct { Next }
func (h *DBTableOrderPromotionsHandler) Do(c *Context) (err error) { fmt.Println(runFuncName(), "写订单优惠信息表...") return }
func runFuncName() string { pc := make([]uintptr, 1) runtime.Callers(2, pc) f := runtime.FuncForPC(pc[0]) return f.Name() }
func main() { nullHandler := &NullHandler{}
nullHandler.SetNext(&ArgumentsHandler{}). SetNext(&AddressInfoHandler{}). SetNext(&CartInfoHandler{}). SetNext(&StockInfoHandler{}). SetNext(&PromotionInfoHandler{}). SetNext(&ShipmentInfoHandler{}). SetNext(&PromotionUseHandler{}). SetNext(&StockSubtractHandler{}). SetNext(&CartDelHandler{}). SetNext(&DBTableOrderHandler{}). SetNext(&DBTableOrderSkusHandler{}). SetNext(&DBTableOrderPromotionsHandler{})
if err := nullHandler.Run(&Context{}); err != nil { fmt.Println("Fail | Error:" + err.Error()) return } fmt.Println("Success") return }
|
代码运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| [Running] go run "../easy-tips/go/src/patterns/responsibility/responsibility-order-submit.go" main.(*ArgumentsHandler).Do 校验参数成功... main.(*AddressInfoHandler).Do 获取地址信息... main.(*AddressInfoHandler).Do 地址信息校验... main.(*CartInfoHandler).Do 获取购物车数据... main.(*StockInfoHandler).Do 获取商品库存信息... main.(*StockInfoHandler).Do 商品库存校验... main.(*PromotionInfoHandler).Do 获取优惠信息... main.(*ShipmentInfoHandler).Do 获取运费信息... main.(*PromotionUseHandler).Do 使用优惠信息... main.(*StockSubtractHandler).Do 扣库存... main.(*CartDelHandler).Do 清理购物车... main.(*DBTableOrderHandler).Do 写订单表... main.(*DBTableOrderSkusHandler).Do 写订单商品表... main.(*DBTableOrderPromotionsHandler).Do 写订单优惠信息表... Success
|
demo2
middlewares
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
| package main
import ( "fmt" )
type Context struct { }
type Handler interface { Do(c *Context) error SetNext(h Handler) Handler Run(c *Context) }
type Next struct { nextHandler Handler }
func (n *Next) SetNext(h Handler) Handler { n.nextHandler = h return h }
func (n *Next) Run(c *Context) { if n.nextHandler != nil { (n.nextHandler).Do(c) (n.nextHandler).Run(c) } }
type NullHandler struct { Next }
func (h *NullHandler) Do(c *Context) error { return nil }
type SignHandler struct { Next }
func (h *SignHandler) Do(c *Context) error { fmt.Println("校验签名成功...") return nil }
type ArgumentsHandler struct { Next }
func (h *ArgumentsHandler) Do(c *Context) error { fmt.Println("校验参数成功...") return nil }
type FrequentHandler struct { Next }
func (h *FrequentHandler) Do(c *Context) error { fmt.Println("校验请求频率成功...") return nil }
func main() { nullHandler := &NullHandler{} argumentsHandler := &ArgumentsHandler{} signHandler := &SignHandler{} frequentHandler := &FrequentHandler{}
nullHandler.SetNext(argumentsHandler).SetNext(signHandler).SetNext(frequentHandler) nullHandler.Run(&Context{})
fmt.Println("----------------------")
middlewares := make([]Handler, 0) middlewares = append(middlewares, nullHandler) middlewares = append(middlewares, argumentsHandler) middlewares = append(middlewares, signHandler) middlewares = append(middlewares, frequentHandler)
for k, handler := range middlewares { if k == 0 { continue } middlewares[k-1].SetNext(handler) } nullHandler.Run(&Context{}) }
|
结语
最后总结下,「责任链模式(改)」抽象过程的核心是:
- 按职责划分:业务逻辑归类,收敛的过程。
- 对象链:把收敛之后的业务对象构成对象链,依次被执行。
1 2 3
| 特别说明: 1. 我的代码没有`else`,只是一个在代码合理设计的情况下自然而然无限接近或者达到的结果,并不是一个硬性的目标,务必较真。 2. 本系列的一些设计模式的概念可能和原概念存在差异,因为会结合实际使用,取其精华,适当改变,灵活使用。
|
文章列表
Go设计模式实战系列 更多文章 点击此处查看