Golang装饰器模式实现缓存功能
在Golang中如何使用装饰器模式实现缓存功能?我目前有一个函数需要频繁调用,想通过缓存来优化性能,但不想修改原函数逻辑。装饰器模式看起来比较适合这种场景,但不太清楚在Go中如何具体实现。能否提供一个示例代码,展示如何用装饰器包装原函数并添加缓存逻辑?最好能说明缓存键的生成策略和过期处理等细节。另外,这种实现方式在高并发时是否需要考虑线程安全问题?
2 回复
在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))
}
关键点说明:
- 装饰器结构:
CachedService包装了原始服务,实现了相同接口 - 缓存逻辑:使用 map 存储缓存数据,通过读写锁保证并发安全
- 透明性:客户端无需知道缓存的存在,直接调用接口方法即可
这种实现方式遵循开闭原则,无需修改原有代码即可扩展缓存功能,且支持嵌套其他装饰器。


