装饰器模式是一种结构型设计模式。通过装饰器模式可以为一个对象添加额外的功能而不需对其作出调整。

还是通过具体的案例来了解装饰器模式:假设我们开了一家披萨店,现在店里主营两款披萨:

  • 素食狂披萨(Veggie Mania Pizza)
  • 活力豆腐披萨(Peppy Tofu pizza)

以上两款披萨有不同的价格,为获取价格需要定义这样一个接口:

package main

type pizza interface {
getPrice() int
}

然后需要这两款披萨分别创建一个struct并实现getPrice()函数来返回价格。因为定义了getPrice()函数,因此这两款披萨的struct可以视为实现了pizza接口。

现在又有了一些变化:我们为设计了一些特色配料,这些配料也是需要收费的。这样我们需要修改下之前的pizza接口,通过装饰器的形式将配料(topping)的信息给加进去。当前已有的配料为:

  • 番茄酱(TomatoTopping)
  • 奶酪(CheeseTopping)

另外,我们也得注意,加了配料的披萨也是一种新的披萨,所以现在顾客有了更多的选择:

  • 素食狂披萨 + 番茄酱
  • 素食狂披萨 + 奶酪
  • 不加任何配料的素食狂披萨
  • 活力豆腐披萨 + 番茄酱
  • ...

加上配料的信息后情况变得复杂起来了,为每种选择都创建一个新的struct明显是不可行的。装饰器模式是一个不错的解决方案:使用装饰器模式可以在不修改已有的struct的前提下添加额外的功能。要使用装饰器模式,我们需要为每种配料(Topping)分别创建一个struct。配料的struct也需要继承前面的pizza接口并嵌入一个pizza接口的实例。

现在每种披萨以及每种配料都有一个独立的struct了。每种披萨和配料都有各自的价格。当为披萨添加配料的时候,只需要在披萨的价格的基础上加上配料的价格就可以计算出最终的价格。

现在可以看到装饰器模式的作用了:我们不需要对pizza struct做任何调整,只是在pizza对象的基础上做了一些装饰就得到了最终的价格。在这个过程中pizza struct不知道 topping struct的任何信息,只知道自己的价格。

下面是装饰器模型的UML类图:

类图中ConcreteComponent(VeggieMania和PeppyTofu)和ConcreteDecorator(Topping)都实现了Component接口(Pizza),并且ConcreteDecorator还嵌入了一个Component接口的一个实例。

对比我们前面的例子:

  • pizza接口是图中的Component
  • veggieManiapeppyPanner是图中的ConcreteComponent,他们都实现了pizza接口
  • ConcreteDecorator的代表是cheeseToppingtomatoTopping,它们也都实现了pizza接口,同时它们也都嵌入了一个pizza接口的实例

来看看具体的代码吧:

pizza.go

type pizza interface {
getPrice() int
}

peppyTofu.go

type peppyTofu struct {
} func (p *peppyTofu) getPrice() int {
return 20
}

veggeMania.go

type veggieMania struct {
} func (p *veggieMania) getPrice() int {
return 15
}

cheeseTopping.go

type cheeseTopping struct {
pizza pizza
} func (c *cheeseTopping) getPrice() int {
pizzaPrice := c.pizza.getPrice()
return pizzaPrice + 10
}

tomatoTopping.go

type tomatoTopping struct {
pizza pizza
} func (c *tomatoTopping) getPrice() int {
pizzaPrice := c.pizza.getPrice()
return pizzaPrice + 7
}

main.go

func main() {

	veggiePizza := &veggieMania{}

	//Add cheese topping
veggiePizzaWithCheese := &cheeseTopping{
pizza: veggiePizza,
} //Add tomato topping
veggiePizzaWithCheeseAndTomato := &tomatoTopping{
pizza: veggiePizzaWithCheese,
} fmt.Printf("Price of veggieMania pizza with tomato and cheese topping is %d\n", veggiePizzaWithCheeseAndTomato.getPrice()) peppyTofuPizza := &peppyTofu{} //Add cheese topping
peppyTofuPizzaWithCheese := &cheeseTopping{
pizza: peppyTofuPizza,
} fmt.Printf("Price of peppyTofu with tomato and cheese topping is %d\n", peppyTofuPizzaWithCheese.getPrice()) }

输出内容:

Price of veggieMania pizza with tomato and cheese topping is 32
Price of peppyTofu with tomato and cheese topping is 30

代码已上传至GitHub: zhyea / go-patterns / decorator-pattern

END!!!

GoLang设计模式21 - 装饰模式的更多相关文章

  1. 乐在其中设计模式(C#) - 装饰模式(Decorator Pattern)

    原文:乐在其中设计模式(C#) - 装饰模式(Decorator Pattern) [索引页][源码下载] 乐在其中设计模式(C#) - 装饰模式(Decorator Pattern) 作者:weba ...

  2. Golang设计模式—简单工厂模式(Simple Factory Pattern)

    Golang设计模式--简单工厂模式 背景 假设我们在做一款小型翻译软件,软件可以将德语.英语.日语都翻译成目标中文,并显示在前端. 思路 我们会有三个具体的语言翻译结构体,或许以后还有更多,但现在分 ...

  3. 数据结构和算法(Golang实现)(21)排序算法-插入排序

    插入排序 插入排序,一般我们指的是简单插入排序,也可以叫直接插入排序.就是说,每次把一个数插到已经排好序的数列里面形成新的排好序的数列,以此反复. 插入排序属于插入类排序算法. 除了我以外,有些人打扑 ...

  4. Golang 常见设计模式之装饰模式

    想必只要是熟悉 Python 的同学对装饰模式一定不会陌生,这类 Python 从语法上原生支持的装饰器,大大提高了装饰模式在 Python 中的应用.尽管 Go 语言中装饰模式没有 Python 中 ...

  5. .net设计模式之装饰模式

    概述: 装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能.它是通过创建一个包装对象,也就是装饰来包裹真实的对象. 装饰模式的特点: (1) 装饰对象和真实对象有相同的接口.这样 ...

  6. 设计模式之装饰模式(Decorator)摘录

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/fengbingchun/article/details/29237955 23种GOF设计模式一般分 ...

  7. java设计模式之装饰模式

    发现设计模式的学习越来越让自己学习的东西太少了,应该多接触一些东西,多出去走一走. 装饰模式概念: 动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活(大话设计模式) 在不 ...

  8. 【GOF23设计模式】装饰模式

    来源:http://www.bjsxt.com/ 一.[GOF23设计模式]_装饰模式.IO流底层架构.装饰和桥接模式的区别 package com.test.decorator; /** * Com ...

  9. Java——设计模式(装饰模式_IO)

     /* * 装饰设计模式: *  对一组对象的功能进行增强时,就可以使用该模式进行问题的解决; * 装饰和继承都能实现一样的特点:  就是进行功能的扩转增强. * */ public class  ...

随机推荐

  1. List去重复

    HashSet hs = new HashSet(li1); //此时已经去掉重复的数据保存在hashset中 HashSet不能给cbx.datasource赋值,要转成list再赋值 List t ...

  2. 【第十四期】高德go面经

    自我介绍 选一个比较熟悉的项目讲讲 筛选日志的时候,日志格式是不一样的,你们是如何处理的? 处理日志的时候如果日志量比较大会堆积吗?怎么处理的? 日志落盘到机器上,是如何采集的? 采集服务有问题的话可 ...

  3. oeasy教您玩转python - 006 - # hello world

    ​ Hello World! 回忆上次内容 python3 的程序是一个 5.3M 的可执行文件 python3 里面全都是 cpu 指令 可以执行的那种指令 我们可以把指令对应的汇编找到 objdu ...

  4. 「JOI 2014 Final」裁剪线

    做法一 首先将边界也视作四条裁剪线,整个平面作为一张纸,视存在 \(y = -\infty, y = +\infty, x = -\infty, x = +\infty\) 四条直线. 按照纵坐标依次 ...

  5. Mac系统如何显示隐藏文件

    Command+Shift+. 可以显示隐藏文件.文件夹,再按一次,恢复隐藏:finder下使用Command+Shift+G 可以前往任何文件夹,包括隐藏文件夹.

  6. JS 将Table内容导出到Excel(样式设计)

    转载请注明来源:https://www.cnblogs.com/hookjc/ function saveAsExcel(tableID){ var tb = new TableToExcel(tab ...

  7. 关于synchronized(this)中this指的是什么意思

    public class SynchronizedDEmo { public static void main(String[] args) { TestThread tt = new TestThr ...

  8. apt安装zabbix

    下面介绍基于ubuntu18.04,使用apt在ubuntu安装zabbix 4.0.x版本.规划在10.0.0.101主机安装zabbix server,在10.0.0.104安装提供msyql服务 ...

  9. desktopForWin安装

    window环境下搭建appium(win7和win10都试过,能行),这里只说了Android自动化环境.iOS自动化需要MacOS支持. 一.python环境搭建 下载Python 官网下载地址h ...

  10. 暑假撸系统5- Thymeleaf 常用标签的

    上次博客已经是三天前了,后期修补和细化的东西多了,进度没有前几天那么明显了.因为原来工作大多是后端居多,如果非要前台也会选择一些相对对ui依赖比较小的框架,比如extjs,所以这次的基础排版就费劲了, ...