手撸golang 行为型设计模式 责任链模式
2012 年 5 月 25 日
手撸golang 行为型设计模式 责任链模式
缘起
最近复习设计模式
拜读谭勇德的<<设计模式就该这样学>>
本系列笔记拟采用golang练习之
责任链模式
责任链模式(Chain of Responsibility Pattern)将链中每一个节点都看作一个对象, 每个节点处理的请求均不同, 且内部自动维护下一个节点对象。 当一个请求从链式的首端发出时, 会沿着责任链预设的路径依次传递到每一个节点对象, 直至被链中的某个对象处理为止, 属于行为型设计模式。 责任链模式主要适用于以下应用场景。 (1)多个对象可以处理同一请求,但具体由哪个对象处理则在运行时动态决定。 (2)在不明确指定接收者的情况下,向多个对象中的一个提交请求。 (3)可动态指定一组对象处理请求。 (摘自 谭勇德 <<设计模式就该这样学>>)
场景
- 某业务系统, 需要将日志按严重等级(Debug/Info/Error), 分开不同文件
- 码农王二狗, 于是设计了DebugLogger, InfoLogger, ErrorLogger三个日志类
- 业务层根据日志输出等级, 分别调用不同的logger
-
Leader张阿蛋审阅后非常不满意
- 张阿蛋: 狗子, 写个日志还得调用三个类, 业务team的人还不把我们骂得狗血淋头
- 王二狗: …张哥, 那你的意见是?
- 张阿蛋: 就一个ILogger门面接口, 把Debug/Info/Error方法都放进去; 里面用个责任链, Debug/Info/Error各自做一个节点.
- 王二狗: 张哥, 强!
设计
- ILogger: 定义日志器 门面 接口
- tSimpleLogger: 日志器门面, 实现ILogger接口, 内部使用 责任链模式 分别处理Debug/Info/Error请求
- ILoggerFilter: 定义日志责任链节点的接口
- tLoggerFilter: 日志责任链节点, 实现ILoggerFilter接口
- tFileWriter: 具体负责日志输出, 实现io.StringWriter接口
单元测试
chain_responsibility_test.go
package behavioral_patterns import ( "learning/gooop/behavioral_patterns/chain" "testing" ) func Test_ChainResponsibility(t *testing.T) { logger := chain.NewSimpleLogger() logger.Debug("a debug msg") logger.Info("an info msg") logger.Error("an error msg") }
测试输出
$ go test -v chain_responsibility_test.go === RUN Test_ChainResponsibility tFileWriter.WriteString, file=debug.log, msg=DEBUG a debug msg tFileWriter.WriteString, file=info.log, msg=INFO an info msg tFileWriter.WriteString, file=error.log, msg=ERROR an error msg --- PASS: Test_ChainResponsibility (0.00s) PASS ok command-line-arguments 0.002s
ILogger.go
定义日志器门面接口
package chain type ILogger interface { Debug(msg string) Info(msg string) Error(msg string) }
tSimpleLogger.go
日志器门面, 实现ILogger接口, 内部使用责任链模式分别处理Debug/Info/Error请求
package chain type tSimpleLogger struct { chain ILoggerFilter } func NewSimpleLogger() ILogger { vErrorLogger := newLoggerFilter(newFileWriter("error.log"), LEVEL_ERROR, nil) vInfoLogger := newLoggerFilter(newFileWriter("info.log"), LEVEL_INFO, nil) vDebugLogger := newLoggerFilter(newFileWriter("debug.log"), LEVEL_DEBUG, nil) vDebugLogger.Next(vInfoLogger) vInfoLogger.Next(vErrorLogger) return &tSimpleLogger { vDebugLogger, } } func (me *tSimpleLogger) Debug(msg string) { me.chain.Handle(LEVEL_DEBUG, msg) } func (me *tSimpleLogger) Info(msg string) { me.chain.Handle(LEVEL_INFO, msg) } func (me *tSimpleLogger) Error(msg string) { me.chain.Handle(LEVEL_ERROR, msg) }
ILoggerFilter.go
定义日志责任链节点的接口
package chain type LoggingLevel string const LEVEL_DEBUG LoggingLevel = "DEBUG" const LEVEL_INFO LoggingLevel = "INFO" const LEVEL_ERROR LoggingLevel = "ERROR" type ILoggerFilter interface { Next(filter ILoggerFilter) Handle(level LoggingLevel, msg string) }
tLoggerFilter.go
日志责任链节点, 实现ILoggerFilter接口
package chain import ( "fmt" "io" ) type tLoggerFilter struct { writer io.StringWriter level LoggingLevel chain ILoggerFilter } func newLoggerFilter(writer io.StringWriter, level LoggingLevel, filter ILoggerFilter) ILoggerFilter { return &tLoggerFilter{ writer, level, filter, } } func (me *tLoggerFilter) Next(filter ILoggerFilter) { me.chain = filter } func (me *tLoggerFilter) Handle(level LoggingLevel, msg string) { if me.level == level { _,_ = me.writer.WriteString(fmt.Sprintf("%v %s", me.level, msg)) } else { if me.chain != nil { me.chain.Handle(level, msg) } } }
tFileWriter.go
具体负责日志输出, 实现io.StringWriter接口
package chain import ( "fmt" "io" ) type tFileWriter struct { file string } func newFileWriter(file string) io.StringWriter { return &tFileWriter{ file, } } func (me *tFileWriter) WriteString(s string) (n int, e error) { fmt.Printf("tFileWriter.WriteString, file=%s, msg=%s\n", me.file, s) return len(s), nil }
责任链模式小结
责任链模式的优点 (1)将请求与处理解耦。 (2)请求处理者(节点对象)只需关注自己感兴趣的请求进行处理即可,对于不感兴趣的请求,直接转发给下一个节点对象。 (3)具备链式传递处理请求功能,请求发送者不需要知晓链路结构,只需等待请求处理结果即可。 (4)链路结构灵活,可以通过改变链路结构动态地新增或删减责任。 (5)易于扩展新的请求处理类(节点),符合开闭原则。 责任链模式的缺点 (1)责任链太长或者处理时间过长,会影响整体性能。 (2)如果节点对象存在循环引用,则会造成死循环,导致系统崩溃。 (摘自 谭勇德 <<设计模式就该这样学>>)
(end)
有疑问加站长微信联系(非本文作者)