Golang装饰器模式实现缓存功能

在Golang中如何使用装饰器模式实现缓存功能?我目前有一个函数需要频繁调用,想通过缓存来优化性能,但不想修改原函数逻辑。装饰器模式看起来比较适合这种场景,但不太清楚在Go中如何具体实现。能否提供一个示例代码,展示如何用装饰器包装原函数并添加缓存逻辑?最好能说明缓存键的生成策略和过期处理等细节。另外,这种实现方式在高并发时是否需要考虑线程安全问题?

2 回复

在Golang中实现装饰器模式缓存,可以这样搞:

type Cache interface {
    Get(key string) (interface{}, bool)
    Set(key string, value interface{})
}

type UserService interface {
    GetUser(id int) (*User, error)
}

type userService struct{}

func (s *userService) GetUser(id int) (*User, error) {
    // 数据库查询逻辑
    return &User{ID: id}, nil
}

type cachedUserService struct {
    service UserService
    cache   Cache
}

func (s *cachedUserService) GetUser(id int) (*User, error) {
    key := fmt.Sprintf("user_%d", id)
    
    if val, ok := s.cache.Get(key); ok {
        return val.(*User), nil
    }
    
    user, err := s.service.GetUser(id)
    if err != nil {
        return nil, err
    }
    
    s.cache.Set(key, user)
    return user, nil
}

使用方式:

// 创建缓存装饰器
cache := NewSimpleCache() // 简单内存缓存
service := &cachedUserService{
    service: &userService{},
    cache:   cache,
}

user, _ := service.GetUser(1) // 第一次查数据库
user, _ = service.GetUser(1)  // 第二次走缓存

核心思想:用结构体包装原服务,在方法调用前后加入缓存逻辑,不修改原服务代码。

更多关于Golang装饰器模式实现缓存功能的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Golang中实现装饰器模式来添加缓存功能,可以通过定义一个缓存装饰器来包装原始对象,拦截方法调用并添加缓存逻辑。以下是具体实现:

1. 定义接口

type DataService interface {
    GetData(key string) (string, error)
}

2. 实现具体服务

type RealService struct{}

func (s *RealService) GetData(key string) (string, error) {
    // 模拟耗时操作
    time.Sleep(100 * time.Millisecond)
    return "data_for_" + key, nil
}

3. 实现缓存装饰器

type CachedService struct {
    service DataService
    cache   map[string]string
    mutex   sync.RWMutex
}

func NewCachedService(service DataService) *CachedService {
    return &CachedService{
        service: service,
        cache:   make(map[string]string),
    }
}

func (c *CachedService) GetData(key string) (string, error) {
    // 先查缓存
    c.mutex.RLock()
    if data, exists := c.cache[key]; exists {
        c.mutex.RUnlock()
        return data, nil
    }
    c.mutex.RUnlock()

    // 缓存未命中,调用实际服务
    data, err := c.service.GetData(key)
    if err != nil {
        return "", err
    }

    // 写入缓存
    c.mutex.Lock()
    c.cache[key] = data
    c.mutex.Unlock()
    
    return data, nil
}

4. 使用示例

func main() {
    realService := &RealService{}
    cachedService := NewCachedService(realService)

    // 第一次调用(缓存未命中)
    start := time.Now()
    data, _ := cachedService.GetData("user123")
    fmt.Printf("第一次获取: %s, 耗时: %v\n", data, time.Since(start))

    // 第二次调用(缓存命中)
    start = time.Now()
    data, _ = cachedService.GetData("user123")
    fmt.Printf("第二次获取: %s, 耗时: %v\n", data, time.Since(start))
}

关键点说明:

  1. 装饰器结构CachedService 包装了原始服务,实现了相同接口
  2. 缓存逻辑:使用 map 存储缓存数据,通过读写锁保证并发安全
  3. 透明性:客户端无需知道缓存的存在,直接调用接口方法即可

这种实现方式遵循开闭原则,无需修改原有代码即可扩展缓存功能,且支持嵌套其他装饰器。

回到顶部