每周一个GoLang设计模式之Decorator

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

这篇文章遵循GoF书中的脉络,本篇是这个系列的第3篇:Decorator,这周依旧很开心可以有时间来更新这个系列,希望下周可以继续更新。欢迎大家访问[我的博客](http://zuozuohao.github.io/),代码可以在[@Zuozuohao](https://github.com/Zuozuohao/GolangGOFPatterns)下载。 GoF在第二章通过设计一个Lexi的文档编辑器来介绍设计模式的使用,在上一篇文章[每周一个GoLang设计模式之策略模式](http://zuozuohao.github.io/2016/06/30/Golang-Design-Patterns-Startergy/),中我们简单的实现了策略模式的Lexi的文本编辑器。这一周我们将继续沿着GoF的脚步实现下一个设计模式:Decorator模式。 **问题提出** 在解决Lexi文档编辑器的格式化策略问题之后(见[策略模式](http://zuozuohao.github.io/2016/06/30/Golang-Design-Patterns-Startergy/)),GoF提出了一个新的问题: 1.怎么增加边界以界定文档的页面 2.如何增加滚动条以显示同一页面的不同部分 在这里我们会继续使用策略模式的数据和接口类型,代码可以通过GitHub下载。在传统的面向对象的编程语言中,可以通过继承机制创建Composition的子类BorderComposition、ScrollerComposition以及增加组合效果子类BorderScrollerComposition等等。但是这样当修饰子类增加的情况下,组合效果子类就会急剧增加,产生类爆炸现象。 GoF认为对象组合为修饰机制增加了灵活的扩展机制,GoF建议创建基本的图元修饰类型Border和Scroller。然后进行这些类型进行灵活的组合,从而产生复合修饰效果,消除了类爆炸现象,并且提高了程序设计的灵活性。 GoF定义了Glyph的子类MonoGlyph作为“修饰作用图元”的抽象类,然后定义其子类Border和Scroller,整体结构如下图所示。 ![](http://77fkk5.com1.z0.glb.clouddn.com/upload/image/c69e979f438511e697e4525400020562.png) 如此之后我们就可以将Scroller中组合Composition实例,进而将Scroller组合到Border实例中,完成对象修饰的复合效果,如下图所示(当然也可以变动组合顺序,只要最后不影响用户体验,都是可以的)。 ![](http://77fkk5.com1.z0.glb.clouddn.com/upload/image/dce24787438511e697e4525400020562.png) ****Golang数据结构设计** 这里我们把Decoratorer设计为图元修饰类型的通用接口类型,先绘画出将Composition对象,然后将其嵌入到Border和Scroller类型中进行渲染动作,最后输出组合的修饰图元BorderScrollerPage,整体的代码如下所示: ``` type Decoratorer interface { Draw() } type Composition struct {} type Border struct {} type Scroller struct {} type BorderScrollerPage struct { c *Composition s *Scroller b *Border } ``` **Golang接口实现** 在类型接口定义部分,我们只定义了通用的Draw方法、Border类型的DrawBorder方法和Scroller类型的DrawScroller方法,整体接口代码如下: ``` func (c *Composition) Draw() { fmt.Println("Draw Composition") } func (b *Border) Draw() { fmt.Println("do something before drawwing border") b.DrawBorder() } func (b *Border) DrawBorder() { fmt.Println("Draw border") } func (s *Scroller) Draw() { fmt.Println("do something before drawwing scroller") s.DrawScroller() } func (s *Scroller) DrawScroller() { fmt.Println("Draw scroller") } ``` **组合渲染图元的实现** 下面我们进行组合渲染图元的设计,GoF设计了一个内嵌了Scroller的Border图元,这里起名BorderScrollerPage图元,类型和接口定义如下所示(当然这里你可以进行任意顺序的组合): ``` type BorderScrollerPage struct { c *Composition s *Scroller b *Border } func (bs *BorderScrollerPage) Draw() { bs.c.Draw() bs.s.Draw() bs.b.Draw() fmt.Println("Complete BorderScrollerPage") } ``` ****Golang完整代码** ``` package main import ( "fmt" ) type Decoratorer interface { Draw() } type Composition struct {} type Border struct {} type Scroller struct {} type BorderScrollerPage struct { c *Composition s *Scroller b *Border } func (c *Composition) Draw() { fmt.Println("Draw Composition") } func (b *Border) Draw() { fmt.Println("do something before drawwing border") b.DrawBorder() } func (b *Border) DrawBorder() { fmt.Println("Draw border") } func (s *Scroller) Draw() { fmt.Println("do something before drawwing scroller") s.DrawScroller() } func (s *Scroller) DrawScroller() { fmt.Println("Draw scroller") } func (bs *BorderScrollerPage) Draw() { bs.c.Draw() bs.s.Draw() bs.b.Draw() fmt.Println("Complete BorderScrollerPage") } func main() { bs := &BorderScrollerPage{&Composition{}, &Scroller{}, &Border{}} bs.Draw() } ``` 输出: ``` Draw Composition do something before drawwing scroller Draw scroller do something before drawwing border Draw border Complete BorderScrollerPage ``` [点击这里可以试一下](https://play.golang.org/p/NSHX9ke7-S)。 **Decorator** 1.Decorator实际上是给对象增加功能或者职责的模式。 2.这种模式在不破坏现有数据类型的基础上进行灵活扩展。 3.Decorator模式有效避免了继承带来的类爆炸现象。 非常感谢您读完这篇冗长的文章,如有错误之处请指出,我会尽快修改,谢谢! **其他链接** 1. [每周一个Golang设计模式之组合模式](http://zuozuohao.github.io/2016/06/25/Golang-Design-Patterns-Composite/) 2. [每周一个Golang设计模式之策略模式](http://zuozuohao.github.io/2016/06/30/Golang-Design-Patterns-Startergy/) 3. [每周一个Golang设计模式之Decorator](http://zuozuohao.github.io/2016/07/06/Golang-Design-Patterns-Decorator/) 4. [C的面向对象编程](http://zuozuohao.github.io/Series/CSeries/)

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

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

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