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

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

  • 素食狂披萨(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. 「BalkanOI 2018 Day2」Parentrises

    「BalkanOI 2018 Day2」Parentrises part1 显然可以直接贪心. 右括号记-1,左括号记1. 默认起始全部绿色,不染色. 策略如下: 从左往右扫,如果右括号个数大于左括号 ...

  2. 【Android珍藏】推荐10个炫酷的开源库【转】

    感谢大佬:https://www.jianshu.com/p/d608f0228fd4 前言 技术群里面经常有人问到一些炫酷的UI效果实现方法,有时候我都是给一个相同或者相似效果的Github链接,有 ...

  3. 利用babel将es6语法转es5的简单示例

    前言 Babel是一个广泛使用的转码器,可以将ES6代码转为ES5代码,从而在现有环境执行. 这意味着,你可以现在就用ES6编写程序,而不用担心现有环境是否支持. 文件目录结构 生成包管理配置文件pa ...

  4. cloudstack-4.1.5版本最全入门笔记【2022】

    cloudstack简介 CloudStack是一个开源的具有高可用性及扩展性的云计算平台.目前Cloudstack支持管理大部分主流的hypervisors,如KVM,XenServer,VMwar ...

  5. atc工具模拟网络

    通过Facebook开源的atc工具,进行模拟不同的网络情况,如图: 目前不支持python3 相关网址: ATC http://facebook.github.io/augmented-traffi ...

  6. Solution -「CF 757F」Team Rocket Rises Again

    \(\mathcal{Description}\)   link.   给定 \(n\) 个点 \(m\) 条边的无向图和一个源点 \(s\).要求删除一个不同与 \(s\) 的结点 \(u\),使得 ...

  7. 趣谈IO多路复用的本质

    在<轻松搞懂5种IO模型>中,我发起了一个投票. 答案是[同步IO多路复用].目前,60%的朋友答对了.原因这里解释一下. 同步和异步的概念区别 同步:线程自己去获取结果.(一个线程) 异 ...

  8. php 利用 fsockopen GET/POST 提交表单及上传文件

    1.GET get.php <?php$host = 'demo.fdipzone.com';$port = 80;$errno = '';$errstr = '';$timeout = 30; ...

  9. Linux性能优化之磁盘I/O性能指标

    讨论指标之前,得先解决两个概念:文件系统和磁盘I/O栈. 文件系统是什么?文件系统是在磁盘的基础上,提供了一个用来管理文件的树状结构.简言之,文件系统是树状结构,一种数据结构~逻辑上的概念.磁盘大家都 ...

  10. RocketMQ的invokeSync call timeout异常的解决办法

    缘起 在RocketMQ客户端的DefaultMQPushConsumer的start方法被执行时,时不时会报出invokeSync call timeout异常,如下: Caused by: jav ...