GoLang设计模式15 - 策略模式

策略模式是一种行为型设计模式。通过策略模式,可以在运行时修改一个对象的行为。

接下来仍然是通过例子来了解策略模式。比如说内存缓存,这是我们在开发中经常使用的东西,大家应该都有一定的了解,接下来就用内存缓存来说明下如何使用策略模式。

向内存里存东西对于GoLang来说算是比较简单的事情,通过Map就可以做到,不过还是建议创建一个Cache struct来稍稍封装一下。不过我们都知道,内存缓存占用的空间是有上限的。当快达到上限时,就需要清理一下缓存中已有的内容。如下是清理缓存的一些常见的算法:

  • LRU(Least Recently Used):清理最近使用的最少的那些缓存数据
  • FIFO(First In First Out):清理最早放入缓存的那些数据
  • LFU(Least Frequently Used):清理使用频率最低的那部分数据

现在的问题是如何将Cache和清理算法解耦,这样在运行时就可以调整清理缓存的算法了。但也要注意,在添加新的清理算法时,不应该改动Cache。这时候就需要用到策略模式了。策略模式建议为相同业务的各种算法创建一个算法组,然后将每种算法都封装起来,使之有相同的接口,并且不同算法之间可以互换。清理缓存的算法的接口就可以命名为:evictionAlgo

然后将evictionAlgo接口嵌入Cache中就可以了。

不同于让Cache直接自己继承evictionAlgo接口,现在可以通过evictionAlgo接口来组装不同的清理算法。因为evictionAlgo是一个接口,这样在运行的时候就可以将之赋值为LRU、FIFO或LFU,而不需要对Cache struct做任何调整。

现在捋一下什么时候使用策略模式:

  1. 当一个对象需要提供不同的行为,而又不想在运行时修改对象时
  2. 当想在运行时选择不同的行为而又不想写大量的条件语句时
  3. 当为同一种行为准备了不同的算法时

下面是策略模式的UML类图:

这里是我们当前这个内存缓存案例的UML图:

具体代码如下:

evictionAlgo.go:

1
2
3
type evictionAlgo interface {
    evict(c *cache)
}

lru.go:

1
2
3
4
5
6
7
8
import "fmt"
 
type lru struct {
}
 
func (l *lru) evict(c *cache) {
    fmt.Println("Evicting by lru strategy")
}

fifo.go:

1
2
3
4
5
6
7
8
import "fmt"
 
type fifo struct {
}
 
func (l *fifo) evict(c *cache) {
    fmt.Println("Evicting by fifo strategy")
}

lfu.go

1
2
3
4
5
6
7
8
import "fmt"
 
type lfu struct {
}
 
func (l *lfu) evict(c *cache) {
    fmt.Println("Evicting by lfu strategy")
}

cache.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
type cache struct {
    storage      map[string]string
    evictionAlgo evictionAlgo
    capacity     int
    maxCapacity  int
}
 
func initCache(e evictionAlgo) *cache {
    storage := make(map[string]string)
    return &cache{
        storage:      storage,
        evictionAlgo: e,
        capacity:     0,
        maxCapacity:  2,
    }
}
 
func (c *cache) setEvictionAlgo(e evictionAlgo) {
    c.evictionAlgo = e
}
 
func (c *cache) add(key, value string) {
    if c.capacity == c.maxCapacity {
        c.evict()
    }
    c.capacity++
    c.storage[key] = value
}
 
func (c *cache) get(key string) {
    delete(c.storage, key)
}
 
func (c *cache) evict() {
    c.evictionAlgo.evict(c)
    c.capacity--
}

main.go

1
2
3
4
5
6
7
8
9
10
11
12
13
func main() {
    lfu := &lfu{}
    cache := initCache(lfu)
    cache.add("a", "1")
    cache.add("b", "2")
    cache.add("c", "3")
    lru := &lru{}
    cache.setEvictionAlgo(lru)
    cache.add("d", "4")
    fifo := &fifo{}
    cache.setEvictionAlgo(fifo)
    cache.add("e", "5")
}

执行后的输出内容为:

1
2
3
Evicting by lfu strategy
Evicting by lru strategy
Evicting by fifo strategy

代码已上传至GitHub: zhyea / go-patterns / strategy-pattern

END!!

posted @   robin·张  阅读(490)  评论(1编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 25岁的心里话
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
点击右上角即可分享
微信分享提示