掘金 后端 ( ) • 2024-04-30 11:35

theme: smartblue

该系列文章总结常见设计模式的概念、使用场景与Go的实现方案,,但实际上Go语言并不需要刻意地去过度使用设计模式,反而与Go大道至简地思想冲突。

本篇介绍装饰器模式


装饰器模式概念,主要特点

简单来说装饰器其实就是对原本有的一个系统进行一个装饰,而这个装饰往往是无侵入的,原系统依然可以跟往常一样去使用。就比如原有系统是一杯水,但是现在我不满足于这一杯水的功能,此时我就可以通过装饰器模式去在原有水的系统下去添加一些新的东西,例如奶、茶,这样它就被装饰成了一杯奶茶......装饰器模式能够动态地为对象增加某种特定的附属能力,相比于继承模式显得更加灵活,且符合开闭原则,可以作为继承模式的一种有效补充手段.

一开始有杯水(纯净水类),如果需要不断的为这个水增添功能从而变成一个新功能的水,就需要一个装饰器的类,来动态的给一个类额外添加一个指定的功能,而生成另一个类,但原先的类又没有改变,不影响原有系统的稳定。

在装饰器模式中,水、奶水、茶水,奶茶都是一个构件。

“奶装饰器”、“茶装饰器”是装饰器也是一个构件

image.png

场景举例:

说了概念也没有那么直观,曾经我写过一个localCache的中间件,其主要设计风格就是用的装饰器模式

首先针对这么一个接口

type Cache interface {
	Get(ctx context.Context, key string) ([]byte, error)
	Set(ctx context.Context, key string, value []byte,
		expiration time.Duration) error
	Delete(ctx context.Context, key string) error
	LoadAndDelete(ctx context.Context, key string) ([]byte, error)
}

首先对它进行了基本实现


type MapCache struct { // mapCache 实现Cache接口
	lock          sync.RWMutex                 //  避免并发读写
	data          map[string]*item             // 缓存数据 map
	onEvicted     func(key string, val []byte) //回调函数
	close         chan struct{}                // close信号,使用者可以主动关闭缓存
	closed        bool                         //close 标识
	cycleInterval time.Duration                //定期轮询
}

func (m *MapCache) Get(ctx context.Context, key string) ([]byte, error)
func (m *MapCache) Set(ctx context.Context, key string, value []byte, expiration time.Duration) error
func (m *MapCache) Delete(ctx context.Context, key string) error 
func (m *MapCache) LoadAndDelete(ctx context.Context, key string) ([]byte, error)


对于这个缓存系统可能目前就能使用个大致功能,但是此时我突然想优化一个,我想加加一个可以设置最大内存限制,超过了限制通过fifo、lru、lfu手段进行内存淘汰。内存限制也可以是kv对个数,也可以是总的字节数限制。

假如是我是希望有缓存kv对个数的上限的一个localcache类,那我们就可以像这样去实现装饰器模式去装饰原本的系统

type MaxCntCache struct {
	*MapCache   // 原本的cache中间件
	mutex  sync.Mutex
	MaxCnt int32
	Cnt    int32
	keys   *list.LinkedList[string] //LinkedList实现LRU
}


func (c *MaxCntCache) Set(ctx context.Context,
	key string, val []byte, expiration time.Duration) error {
        /*
        需要进行装饰的逻辑,如检查当前kv对是否超过了阈值。。。。
        */
        
        // c.MapCache.Set()...  // 执行原本的set方法
        }

在新的支持限制kv对个数cache中间件中我们是将mapCache组合了进来,然后对Set方法进行了重写,而这个重写是不会影响原有的set方法的。这样我们就装饰出了一个带kv对限制的cache


以上就是我对装饰器模式的见解