1. 什么是「策略模式」?
个人的理解就是, 实现同一件事情, 可能有不同的算法/策略, 通过使用策略模式, 可以让策略的选择更平滑, 添加/删除一个策略更方便(因为符合开闭原则, 扩展性更好).
2. 策略模式的实现
首先要定义一个策略接口, 有若干不同策略的具体实现去实现这个接口. 然后对于策略的使用者, 只需要依赖抽象的策略接口, 然后通过一个SetStrategy()
设置策略的方法, 来指明当前抽象接口选择哪个具体实现(这也是多态的体现). 最后调用抽象策略接口定义的具体方法即可.
2.1 具体场景Demo
光这样说肯定不好理解, 下面我来举1个例子, 这个例子的场景是我自己想的, 不是抄的.
篮球场上有多种不同的防守战术, 我们认为每一种战术是一个「策略」.
首先定义防守策略接口:
golang
package interfaces
type Defend interface {
Defend()
}
然后写几个具体实现, 意思一下hhh
golang
// strategy 1
type Focus struct{}
func (f *Focus) Defend() {
fmt.Println("执行盯人包夹防守战术")
}
// strategy 2
type OneOnOne struct{}
func (o *OneOnOne) Defend() {
fmt.Println("执行一对一防守战术")
}
// strategy 3
type TwoThree struct{}
func (t *TwoThree) Defend() {
fmt.Println("执行二三联防战术")
}
然后通过一个很多文章上叫Context的类, 去承担 设置策略 + 执行策略 的工作. 我们这里搞了一个 Coach
教练类, 来设置防守战术 并 敦促队员执行.
可以看到, Coach
类内部确实只依赖接口, Coach
对策略的增加/减少是无感的, 扩展性好.
golang
type Coach struct {
name string
defendStrategy interfaces.Defend
}
func (c *Coach) SetDefendStrategy(strategy interfaces.Defend) {
fmt.Printf("%s 切换防守策略\n", c.name)
c.defendStrategy = strategy
}
func (c *Coach) ExecuteDefend() {
c.defendStrategy.Defend()
}
func NewCoach(name string) *Coach {
return &Coach{
name: name,
}
}
最后我们执行main函数, 并看一下效果:
golang
func main() {
coach := NewCoach("波波维奇")
coach.SetDefendStrategy(&strategies.Focus{})
coach.ExecuteDefend()
coach.SetDefendStrategy(&strategies.OneOnOne{})
coach.ExecuteDefend()
}

程序运行成功, 但是我们注意到, 在main函数中, 我们确实要创建策略的具体实现的实例来作为参数传入, 这一点也被归为策略模式的缺点之一. 但是我认为, 不这么写还能怎么写呢, 策略模式总比直接依赖具体实现的方式要强得多.
3. 策略模式的优缺点
3.1 优点
- 符合开闭原则, 扩展性好, 因此在多人合作的时候, 代码易于维护(增加策略的时候, 只需要关注策略的具体实现)
- 可以在运行时切换策略.
- 可以将策略的具体实现和策略的使用这两部分代码隔离开来.
3.2 缺点
- 每种策略都对外暴露. (如果这也算缺点的话)
4. 参考
- 《设计模式》: refactoringguru.cn/design-patt...
- 刘丹冰, Easy设计模式: www.bilibili.com/video/BV1Eg...
5. 题外话
一点思考, 我之前学习的时候, 总是有一个地方不懂就钻牛角尖总想弄懂, 可是有些问题弄懂了面试也不问, 过了一段时间也忘记了, 对写更优雅的代码也没有帮助. 那么不如松弛一点, 尽力弄懂当前阶段能弄懂的即可, 一本武林秘籍, 给不同修为的人看, 他们能领悟的东西也是不同的吗.
杜绝"一个知识点只看一次, 一次看透"这个思想, 小猪乔治跟自己说.