策略模式
定义一系列算法,并将每种算法分别放入独立的类中,以使算法的对象能够相互替换。
模型说明
https://pic8.58cdn.com.cn/nowater/webim/big/n_v20eecba43befa4edfa1e4d3b702e7f737.png
- 上下文(Context)维护指向具体策略的引用,且仅通过策略接口与该对象进行交流。
- 策略(Strategy)接口是所有具体策略的通用接口,它声明了一个上下文用于执行策略的方法。
- 具体策略(Concrete Strategies)实现了上下文所用算法的各种不同变体。
- 当上下文需要运行算法时,它会在其已连接的策略对象上调用执行方法。上下文不清楚其所涉及的策略类型与算法的执行方式。
- 客户端(Client)会创建一个特定策略对象并将其传递给上下文。上下文则会提供一个设置器以便客户端在运行时替换相关联的策略。
优缺点
1.优点
- 你可以在运行时切换对象内的算法。
- 你可以将算法的实现和使用算法的代码隔离开来。
- 你可以使用组合来代替继承。
- 开闭原则: 你无需对上下文进行修改就能够引入新的策略。
2.缺点
- 如果你的算法极少发生改变, 那么没有任何理由引入新的类和接口。 使用该模式只会让程序过于复杂。
- 客户端必须知晓策略间的不同——它需要选择合适的策略。
- 许多现代编程语言支持函数类型功能, 允许你在一组匿名函数中实现不同版本的算法。 这样, 你使用这些函数的方式就和使用策略对象时完全相同, 无需借助额外的类和接口来保持代码简洁。
使用场景
- 当你想使用对象中各种不同的算法变体,并希望能在运行时切换算法时,可使用策略模式。
- 当你有许多仅在执行某些行为时略有不同的相似类时,可使用策略模式。
- 如果算法在上下文的逻辑中不是特别重要,使用该模式能将类的业务逻辑与其算法实现细节隔离开来。
- 当类中使用了复杂条件运算符以在同一算法的不同变体中切换时,可使用该模式。
参考代码
构建内存缓存的情形。
- 最少最近使用 (LRU): 移除最近使用最少的一条条目。
- 先进先出 (FIFO): 移除最早创建的条目。
- 最少使用 (LFU): 移除使用频率最低一条条目。
// evictionAlgo.go: 策略接口 package main type EvictionAlgo interface { evict(c *Cache) }
// fifo.go: 具体策略 package main import "fmt" type Fifo struct { } func (l *Fifo) evict(c *Cache) { fmt.Println("Evicting by fifo strtegy") }
// lru.go: 具体策略 package main import "fmt" type Lru struct { } func (l *Lru) evict(c *Cache) { fmt.Println("Evicting by lru strtegy") }
// lfu.go: 具体策略 package main import "fmt" type Lfu struct { } func (l *Lfu) evict(c *Cache) { fmt.Println("Evicting by lfu strtegy") }
// cache.go: context package main 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 客户端 package main 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") }
output:
Evicting by lfu strtegy Evicting by lru strtegy Evicting by fifo strtegy