Go 装饰器模式学习文档
目录
- 装饰器模式简介
- Go 中实现装饰器的方式
- 使用 logrus 记录日志
- 示例:为函数添加日志装饰器
- 多个装饰器的组合
- 最佳实践
- 总结
1. 装饰器模式简介
装饰器模式是一种结构型设计模式,它允许向一个对象动态地添加新的行为,而不需要修改该对象的基类或使用继承。装饰器模式通过创建一个包装对象来包裹真实的对象,提供额外的功能。
在 Go 中,装饰器模式通常通过函数式编程的方式实现,利用 Go 的函数作为一等公民的特性,我们可以创建高阶函数来包装其他函数,添加额外的行为。
装饰器模式的优点:
- 在不改变原有对象结构的情况下,动态地添加功能
- 遵循开闭原则(对扩展开放,对修改关闭)
- 可以灵活地组合多个装饰器
2. Go 中实现装饰器的方式
在 Go 中,装饰器通常是通过高阶函数实现的。高阶函数是指接受函数作为参数或返回函数的函数。下面是一个简单的装饰器示例:
// 装饰器函数,接受一个函数并返回一个包装后的函数
func decorator(f func()) func() {return func() {fmt.Println("装饰器:函数调用前")f()fmt.Println("装饰器:函数调用后")}
}func hello() {fmt.Println("Hello, World!")
}func main() {// 使用装饰器包装hello函数decoratedHello := decorator(hello)decoratedHello()
}
输出:
装饰器:函数调用前
Hello, World!
装饰器:函数调用后
3. 使用 logrus 记录日志
在 Go 中,logrus
是一个流行的结构化日志库,提供了丰富的功能。首先,我们需要安装 logrus
:
go get github.com/sirupsen/logrus
下面是一个简单的 logrus
使用示例:
package mainimport ("github.com/sirupsen/logrus"
)func main() {// 创建一个新的logger实例logger := logrus.New()// 设置日志级别logger.SetLevel(logrus.DebugLevel)// 设置日志格式为JSONlogger.SetFormatter(&logrus.JSONFormatter{})// 记录不同级别的日志logger.Debug("这是一条调试信息")logger.Info("这是一条普通信息")logger.Warn("这是一条警告信息")logger.Error("这是一条错误信息")// 记录带字段的日志logger.WithFields(logrus.Fields{"event": "test","topic": "logging",}).Info("这是一条带字段的日志")
}
4. 示例:为函数添加日志装饰器
现在,我们将结合装饰器模式和 logrus
来创建一个日志装饰器,用于记录函数的调用信息。
package mainimport ("fmt""time""github.com/sirupsen/logrus"
)// 创建一个logger实例
var logger = logrus.New()// 初始化logger
func init() {// 设置日志级别logger.SetLevel(logrus.DebugLevel)// 设置日志格式为JSONlogger.SetFormatter(&logrus.JSONFormatter{})
}// 日志装饰器
func logDecorator(name string, f func()) func() {return func() {start := time.Now()logger.WithFields(logrus.Fields{"function": name,"event": "start","time": start.Format(time.RFC3339),}).Info("Function call started")f()duration := time.Since(start)logger.WithFields(logrus.Fields{"function": name,"event": "end","duration": duration.String(),}).Info("Function call completed")}
}// 示例函数1
func processData() {logger.Info("Processing data...")time.Sleep(100 * time.Millisecond)logger.Info("Data processed successfully")
}// 示例函数2
func saveToDatabase() {logger.Info("Saving to database...")time.Sleep(150 * time.Millisecond)logger.Info("Data saved successfully")
}func main() {// 使用装饰器包装函数decoratedProcessData := logDecorator("processData", processData)decoratedSaveToDatabase := logDecorator("saveToDatabase", saveToDatabase)// 调用装饰后的函数decoratedProcessData()decoratedSaveToDatabase()
}
输出示例(JSON格式):
{"event":"start","function":"processData","level":"info","msg":"Function call started","time":"2023-05-20T10:00:00+08:00"}
{"level":"info","msg":"Processing data..."}
{"level":"info","msg":"Data processed successfully"}
{"duration":"100.123ms","event":"end","function":"processData","level":"info","msg":"Function call completed"}
{"event":"start","function":"saveToDatabase","level":"info","msg":"Function call started","time":"2023-05-20T10:00:00+08:00"}
{"level":"info","msg":"Saving to database..."}
{"level":"info","msg":"Data saved successfully"}
{"duration":"150.456ms","event":"end","function":"saveToDatabase","level":"info","msg":"Function call completed"}
5. 多个装饰器的组合
装饰器模式的一个强大之处在于可以组合多个装饰器,每个装饰器负责一个特定的功能。下面我们创建多个装饰器,并演示如何组合它们:
package mainimport ("fmt""os""time""github.com/sirupsen/logrus"
)// 创建一个logger实例
var logger = logrus.New()// 初始化logger
func init() {// 设置日志级别logger.SetLevel(logrus.DebugLevel)// 设置日志格式为JSONlogger.SetFormatter(&logrus.JSONFormatter{})// 设置输出到标准输出logger.SetOutput(os.Stdout)
}// 日志装饰器
func logDecorator(name string) func(func()) func() {return func(f func()) func() {return func() {start := time.Now()logger.WithFields(logrus.Fields{"function": name,"event": "start","time": start.Format(time.RFC3339),}).Info("Function call started")f()duration := time.Since(start)logger.WithFields(logrus.Fields{"function": name,"event": "end","duration": duration.String(),}).Info("Function call completed")}}
}// 计时装饰器
func timingDecorator(name string) func(func()) func() {return func(f func()) func() {return func() {start := time.Now()logger.WithField("function", name).Info("Timing started")f()duration := time.Since(start)logger.WithFields(logrus.Fields{"function": name,"duration": duration.String(),}).Info("Timing completed")}}
}// 错误处理装饰器
func errorHandlingDecorator(name string) func(func()) func() {return func(f func()) func() {return func() {defer func() {if r := recover(); r != nil {logger.WithFields(logrus.Fields{"function": name,"error": r,}).Error("Function panicked")}}()f()}}
}// 示例函数
func processData() {logger.Info("Processing data...")time.Sleep(100 * time.Millisecond)logger.Info("Data processed successfully")
}// 可能出错的函数
func riskyOperation() {logger.Info("Starting risky operation...")time.Sleep(50 * time.Millisecond)// 模拟一个错误panic("something went wrong")
}// 装饰器工厂函数,用于组合多个装饰器
func decorate(f func(), decorators ...func(func()) func()) func() {decorated := f// 按照相反的顺序应用装饰器,这样第一个装饰器将是最外层的for i := len(decorators) - 1; i >= 0; i-- {decorated = decorators[i](decorated)}return func() {decorated()}
}func main() {// 组合多个装饰器decoratedProcessData := decorate(processData,logDecorator("processData"),timingDecorator("processData"),errorHandlingDecorator("processData"),)decoratedRiskyOperation := decorate(riskyOperation,logDecorator("riskyOperation"),timingDecorator("riskyOperation"),errorHandlingDecorator("riskyOperation"),)// 调用装饰后的函数decoratedProcessData()decoratedRiskyOperation()
}
输出示例(JSON格式):
{"event":"start","function":"processData","level":"info","msg":"Function call started","time":"2023-05-20T10:00:00+08:00"}
{"function":"processData","level":"info","msg":"Timing started"}
{"level":"info","msg":"Processing data..."}
{"level":"info","msg":"Data processed successfully"}
{"duration":"100.123ms","function":"processData","level":"info","msg":"Timing completed"}
{"duration":"100.123ms","event":"end","function":"processData","level":"info","msg":"Function call completed"}
{"event":"start","function":"riskyOperation","level":"info","msg":"Function call started","time":"2023-05-20T10:00:00+08:00"}
{"function":"riskyOperation","level":"info","msg":"Timing started"}
{"level":"info","msg":"Starting risky operation..."}
{"duration":"50.456ms","function":"riskyOperation","level":"info","msg":"Timing completed"}
{"error":"something went wrong","function":"riskyOperation","level":"error","msg":"Function panicked"}
{"duration":"50.456ms","event":"end","function":"riskyOperation","level":"info","msg":"Function call completed"}
6. 最佳实践
-
单一职责原则:每个装饰器应该只负责一个特定的功能,如日志记录、计时、错误处理等。
-
装饰器顺序:装饰器的应用顺序很重要。通常,最外层的装饰器最先执行,最内层的装饰器最后执行。
-
性能考虑:装饰器会引入额外的开销,特别是在高频调用的函数上。应该权衡装饰器带来的价值和性能开销。
-
错误处理:确保装饰器能够正确处理和传播错误,避免掩盖原始错误。
-
上下文信息:在装饰器中记录足够的上下文信息,以便于调试和监控。
-
可配置性:使装饰器可配置,例如通过选项模式允许用户自定义日志级别、格式等。
-
测试:为装饰器编写单元测试,确保它们在各种情况下都能正常工作。
7. 总结
Go 中的装饰器模式是一种强大的技术,它允许我们以非侵入性的方式为函数添加额外的功能。通过结合 logrus
这样的日志库,我们可以轻松地实现日志记录、性能监控、错误处理等功能。
装饰器模式的主要优点:
- 不需要修改原有函数即可添加新功能
- 可以灵活地组合多个装饰器
- 遵循开闭原则,对扩展开放,对修改关闭
在实际应用中,装饰器模式常用于:
- 日志记录
- 性能监控和计时
- 认证和授权
- 缓存
- 错误处理和恢复
- 事务管理
通过合理地使用装饰器模式,我们可以使代码更加模块化、可维护和可扩展。