每周一个GoLang设计模式之策略模式

Zuozuohao · · 1517 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

这篇文章遵循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/)

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1517 次点击  ∙  1 赞  
加入收藏 微博
3 回复  |  直到 2016-06-30 12:48:22
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传