每周一个Golang设计模式之Decorator

这篇文章遵循GoF书中的脉络,本篇是这个系列的第3篇:Decorator,这周依旧很开心可以有时间来更新这个系列,希望下周可以继续更新。欢迎大家访问我的博客,代码可以在@Zuozuohao下载。

GoF在第二章通过设计一个Lexi的文档编辑器来介绍设计模式的使用,在上一篇文章每周一个GoLang设计模式之策略模式,中我们简单的实现了策略模式的Lexi的文本编辑器。这一周我们将继续沿着GoF的脚步实现下一个设计模式:Decorator模式。

问题提出

在解决Lexi文档编辑器的格式化策略问题之后(见策略模式),GoF提出了一个新的问题:

怎么增加边界以界定文档的页面
如何增加滚动条以显示同一页面的不同部分

在这里我们会继续使用策略模式的数据和接口类型,代码可以通过GitHub下载。在传统的面向对象的编程语言中,可以通过继承机制创建Composition的子类BorderComposition、ScrollerComposition以及增加组合效果子类BorderScrollerComposition等等。但是这样当修饰子类增加的情况下,组合效果子类就会急剧增加,产生类爆炸现象。

GoF认为对象组合为修饰机制增加了灵活的扩展机制,GoF建议创建基本的图元修饰类型Border和Scroller。然后进行这些类型进行灵活的组合,从而产生复合修饰效果,消除了类爆炸现象,并且提高了程序设计的灵活性。

GoF定义了Glyph的子类MonoGlyph作为“修饰作用图元”的抽象类,然后定义其子类Border和Scroller,整体结构如下图所示。

如此之后我们就可以将Scroller中组合Composition实例,进而将Scroller组合到Border实例中,完成对象修饰的复合效果,如下图所示(当然也可以变动组合顺序,只要最后不影响用户体验,都是可以的)。

**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

点击这里可以试一下

Decorator

Decorator实际上是给对象增加功能或者职责的模式。
这种模式在不破坏现有数据类型的基础上进行灵活扩展。
Decorator模式有效避免了继承带来的类爆炸现象。

非常感谢您读完这篇冗长的文章,如有错误之处请指出,我会尽快修改,谢谢!

其他链接

  1. 每周一个Golang设计模式之组合模式
  2. 每周一个Golang设计模式之策略模式
  3. 每周一个Golang设计模式之Decorator
  4. C的面向对象编程

共 1 个回复


billiebing

牛!学习了。请持续分享!

# 0