这篇文章遵循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/)
有疑问加站长微信联系(非本文作者)