Go 装饰器模式学习文档

目录

  1. 装饰器模式简介
  2. [Go 中实现装饰器的方式](#Go 中实现装饰器的方式)
  3. [使用 logrus 记录日志](#使用 logrus 记录日志)
  4. 示例:为函数添加日志装饰器
  5. 多个装饰器的组合
  6. 最佳实践
  7. 总结

1. 装饰器模式简介

装饰器模式是一种结构型设计模式,它允许向一个对象动态地添加新的行为,而不需要修改该对象的基类或使用继承。装饰器模式通过创建一个包装对象来包裹真实的对象,提供额外的功能。

在 Go 中,装饰器模式通常通过函数式编程的方式实现,利用 Go 的函数作为一等公民的特性,我们可以创建高阶函数来包装其他函数,添加额外的行为。

装饰器模式的优点:

  • 在不改变原有对象结构的情况下,动态地添加功能
  • 遵循开闭原则(对扩展开放,对修改关闭)
  • 可以灵活地组合多个装饰器

2. Go 中实现装饰器的方式

在 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

bash 复制代码
go get github.com/sirupsen/logrus

下面是一个简单的 logrus 使用示例:

go 复制代码
package main

import (
    "github.com/sirupsen/logrus"
)

func main() {
    // 创建一个新的logger实例
    logger := logrus.New()
    
    // 设置日志级别
    logger.SetLevel(logrus.DebugLevel)
    
    // 设置日志格式为JSON
    logger.SetFormatter(&logrus.JSONFormatter{})
    
    // 记录不同级别的日志
    logger.Debug("这是一条调试信息")
    logger.Info("这是一条普通信息")
    logger.Warn("这是一条警告信息")
    logger.Error("这是一条错误信息")
    
    // 记录带字段的日志
    logger.WithFields(logrus.Fields{
        "event": "test",
        "topic": "logging",
    }).Info("这是一条带字段的日志")
}

4. 示例:为函数添加日志装饰器

现在,我们将结合装饰器模式和 logrus 来创建一个日志装饰器,用于记录函数的调用信息。

go 复制代码
package main

import (
    "fmt"
    "time"
    
    "github.com/sirupsen/logrus"
)

// 创建一个logger实例
var logger = logrus.New()

// 初始化logger
func init() {
    // 设置日志级别
    logger.SetLevel(logrus.DebugLevel)
    
    // 设置日志格式为JSON
    logger.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格式):

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. 多个装饰器的组合

装饰器模式的一个强大之处在于可以组合多个装饰器,每个装饰器负责一个特定的功能。下面我们创建多个装饰器,并演示如何组合它们:

go 复制代码
package main

import (
    "fmt"
    "os"
    "time"
    
    "github.com/sirupsen/logrus"
)

// 创建一个logger实例
var logger = logrus.New()

// 初始化logger
func init() {
    // 设置日志级别
    logger.SetLevel(logrus.DebugLevel)
    
    // 设置日志格式为JSON
    logger.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格式):

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. 最佳实践

  1. 单一职责原则:每个装饰器应该只负责一个特定的功能,如日志记录、计时、错误处理等。

  2. 装饰器顺序:装饰器的应用顺序很重要。通常,最外层的装饰器最先执行,最内层的装饰器最后执行。

  3. 性能考虑:装饰器会引入额外的开销,特别是在高频调用的函数上。应该权衡装饰器带来的价值和性能开销。

  4. 错误处理:确保装饰器能够正确处理和传播错误,避免掩盖原始错误。

  5. 上下文信息:在装饰器中记录足够的上下文信息,以便于调试和监控。

  6. 可配置性:使装饰器可配置,例如通过选项模式允许用户自定义日志级别、格式等。

  7. 测试:为装饰器编写单元测试,确保它们在各种情况下都能正常工作。

7. 总结

Go 中的装饰器模式是一种强大的技术,它允许我们以非侵入性的方式为函数添加额外的功能。通过结合 logrus 这样的日志库,我们可以轻松地实现日志记录、性能监控、错误处理等功能。

装饰器模式的主要优点:

  • 不需要修改原有函数即可添加新功能
  • 可以灵活地组合多个装饰器
  • 遵循开闭原则,对扩展开放,对修改关闭

在实际应用中,装饰器模式常用于:

  • 日志记录
  • 性能监控和计时
  • 认证和授权
  • 缓存
  • 错误处理和恢复
  • 事务管理

通过合理地使用装饰器模式,我们可以使代码更加模块化、可维护和可扩展。

相关推荐
花酒锄作田5 天前
Gin 框架中的规范响应格式设计与实现
golang·gin
西岸行者5 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
悠哉悠哉愿意5 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习
别催小唐敲代码6 天前
嵌入式学习路线
学习
毛小茛6 天前
计算机系统概论——校验码
学习
babe小鑫6 天前
大专经济信息管理专业学习数据分析的必要性
学习·数据挖掘·数据分析
winfreedoms6 天前
ROS2知识大白话
笔记·学习·ros2
在这habit之下6 天前
Linux Virtual Server(LVS)学习总结
linux·学习·lvs
我想我不够好。6 天前
2026.2.25监控学习
学习
im_AMBER6 天前
Leetcode 127 删除有序数组中的重复项 | 删除有序数组中的重复项 II
数据结构·学习·算法·leetcode