这篇文章遵循GoF书中的脉络,本篇是这个系列的第2篇:策略模式(Strategy),这周很开心可以有时间来更新这个系列,希望下周可以继续更新。欢迎大家访问[我的博客](http://zuozuohao.github.io/),代码可以在[@Zuozuohao](https://github.com/Zuozuohao/GolangGOFPatterns)下载。
GoF在第二章通过设计一个Lexi的文档编辑器来介绍设计模式的使用,在上一篇文章[每周一个GoLang设计模式之组合模式](http://zuozuohao.github.io/2016/06/25/Golang-Design-Patterns-Composite/),中我们简单的实现了组合模式的Lexi的文本编辑器。这一周我们将继续沿着GoF的脚步实现下一个设计模式:策略模式(Strategy)。
在软件开发中也常常遇到实现某一个功能有多种算法或者策略,我们可以根据环境或者条件的不同进行选择。一种常用的方法是在用户代码中进行硬编码(Hard Coding),这样给后续的扩展和维护带来大量困难。
在这种情况下就可以使用策略模式,将多种算法进行封装,只暴露给外界固定数目的通用算法接口,这样就可以在后续的工作中在算法内部进行维护和扩展,避免更改用户端的代码实现,从而减小代码维护的成本,降低模块间的耦合程度。
**问题提出**
GoF认为在构建好Lexi文的本编辑器的物理结构之后,面临的一个问题是构造一个
特殊物理结构,该结构对应于一个恰当地格式化了的文档,以便能够在视图中按着指定的方式进行显示。
因为格式化算法趋于复杂化,因而可以考虑将它们包含于文档结构之中,但最好是将它
们彻底独立于文档结构之外。理想情况下,我们能够自由地增加一个Glyph子类而不用考虑格式算法。反过来,增加一个格式算法不应要求修改已有的图元类。
可以定义一个封装格式化算法的对象的类层次结构。类层次结构的根结点将定义支持许多格式化算法的接口,每个子类实现这个接口以执行特定的算法。那时就能让Glyph子类对象自动使用给定算法对象来排列其子图元。
GoF为能封装格式化算法的对象定义一个Compositor类。它的接口可让Compositor获知何时去格式化哪些图元。它所格式化的图元是一个被称为Composition的特定图元的各个子图元。一个Composition在创建时得到一个Compositor子类实例,并在必要的时候(如用户改变文档的时候)让Compositor对它的图元作Compose操作。下图描述了Composition类和Compositor类之间的关系。
![Strategy1.PNG](http://studygolang.qiniudn.com/160630/881216823cc4122f80b9b5ff5f9f22fc.PNG)
一个未格式化的Composition对象只包含组成文档基本内容的可见图元。它并不包含像行
和列这样的决定文档物理结构的图元。Composition对象只在刚被创建并以待格式化的图元进行初始化后,才处于这种状态。
当Composition需要格式化时,调用它的Compositor的Compose操作。Compositor依次遍历Composition的各个子图元,根据分行算法插入新的行和列图元。下图显示了得到的对象结构。图中由Compositor创建和插入到对象结构中的图元以灰色背景显示。
![Strategy2.PNG](http://studygolang.qiniudn.com/160630/4b476432673a2f0cc97b02bf85b59f2e.PNG)
每一个Compositor子类都能实现一个不同的分行算法。例如,一个Simple Compositor可以
执行得很快,而不考虑像文档“色彩”这样深奥的东西。好的色彩意味着文本和空白的平滑
分布。一个TeXCompositor会实现完全的TEX算法,会考虑像色彩这样的东西,而以较长的格式化时间作为代价。
Compositor-Composition类的分离确保了支持文档物理结构的代码和支持不同格式化算法
的代码之间的分离。我们能增加新的Compositor子类而不触及Glyph类,反之亦然。事实上,我们通过给Composition的基本图元接口增加一个settCompositor操作,即可在运行时刻改变分行算法。
****Golang数据结构设计**
这里我们把Composition作为通用类作为暴露类,方便用户进行调用,而具体的格式化执行策略由具体类:
1. ArrayCompositor
2. TexCompositor
3. SimpleCompositor
执行。
```
type Composition struct {}
type ArrayCompositor struct {
*Composition
}
type TexCompositor struct {
*Composition
}
type SimpleCompositor struct {
*Composition
}
```
**Golang接口实现**
我们将Compositer接口作为暴露接口,这个接口对应于GoF策略模式中Compositor的设计。SetCompositer方法赋予不同图元不同的格式化策略,Compose方法执行具体的格式化流程。
```
type Compositer interface {
SetCompositer(composition interface{})
Compose()
}
```
****Golang完整代码**
这里为了避免冗余的代码,没有为每种Compositor数据进行方法覆写,只是利用了匿名嵌入实现了不同Compositor数据的区分,这在实际中实际中是不存在的。
```
package main
import (
"fmt"
"reflect"
)
type Compositer interface {
SetCompositer(composition interface{})
Compose()
}
type Composition struct{}
func (com *Composition) SetCompositer(composition interface{}) {
fmt.Println("Here is seting ", reflect.TypeOf(composition), "before formattig")
}
func (com *Composition) Compose() {
fmt.Println("Here is formatting")
}
func NewCompositor(strategy string) Compositer {
composition := new(Composition)
var g Compositer
switch strategy {
case "Array":
g = &ArrayCompositor{composition}
case "Tex":
g = &TexCompositor{composition}
case "Simple":
g = &SimpleCompositor{composition}
}
return g
}
type ArrayCompositor struct {
*Composition
}
type TexCompositor struct {
*Composition
}
type SimpleCompositor struct {
*Composition
}
func main() {
c1 := NewCompositor("Array")
c1.SetCompositer(c1)
c1.Compose()
}
```
输出:
```
Here is seting *main.ArrayCompositor before formattig
Here is formatting
```
当然你可以随意改变一下main函数里面的策略执行方式,[点击这里可以试一下](https://play.golang.org/p/VViM6o1ZLM)。
**策略模式**
1. 策略模式是对路径选择算法的封装,对外暴露一个通用的接口,降低了客户端代码与算法代码间的耦合程度,从而为后续的开发和维护带来了极大的方便和效益。
2. 模式的主要参与者是Strategy对象(这些对象中封装了不同的算法)和它们的操作环境
3. Strategy模式应用的关键点在于为Strategy和它的环境设计足够通用的接口。
非常感谢您读完这篇冗长的文章,如有错误之处请指出,我会尽快修改,谢谢!
**相关链接**
1. [每周一个Golang设计模式之组合模式](http://zuozuohao.github.io/2016/06/25/Golang-Design-Patterns-Composite/)
**其他链接**
[C的面向对象编程](http://zuozuohao.github.io/Series/CSeries/)