美文网首页设计模式
手撸golang 行为型设计模式 责任链模式

手撸golang 行为型设计模式 责任链模式

作者: 老罗话编程 | 来源:发表于2021-02-07 23:20 被阅读0次

    手撸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)

    相关文章

      网友评论

        本文标题:手撸golang 行为型设计模式 责任链模式

        本文链接:https://www.haomeiwen.com/subject/uzgtxltx.html