16

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

 3 years ago
source link: https://studygolang.com/articles/33245
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

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

有疑问加站长微信联系(非本文作者)

eUjI7rn.png!mobile

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK