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

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

  • 素食狂披萨(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. django入门 03 模板语法

    变量值从views.py传入html 基本语法 {{ abc }} 变量abc外,用双大括号包裹 {% for item in abc %} 语句外,用大括号+百分号包裹 1. 传递字符串 views ...

  2. [USACO18DEC]The Cow Gathering P

    首先可以思考一下每次能删去的点有什么性质. 不难发现,每次能删去的点都是入度恰好为 \(1\) 的那些点(包括 \(a_i \rightarrow b_i\) 的有向边). 换句话说,每次能删去的点既 ...

  3. Java协变、逆变、类型擦除

    协变.逆变 定义 Java中String类型是继承自Object的,姑且记做String ≦ Object,表示String是Object的子类型,String的对象可以赋给Object的对象.而Ob ...

  4. vue3 最长递增子序列 diff优化

    //vue3优化版(回头我会完善下算法思路) function getSequence(arr) { const p = arr.slice() const result = [0] let i, j ...

  5. storyboard文件的认识

    - 作用:描述软件界面 - 程序启动的简单过程     - 程序一启动,就会加载`Main.storyboard`文件     - 会创建箭头所指的控制器,并且显示控制器所管理的软件界面 - 配置程序 ...

  6. Block基本概念

    1.什么是Block Block是iOS中一种比较特殊的数据类型 Block是苹果官方特别推荐使用的数据类型, 应用场景比较广泛 动画 多线程 集合遍历 网络请求回调 Block的作用 用来保存某一段 ...

  7. Scala中的运算符

    Scala和Java中的运算符用法基本一致. 一.区别 1."=="和"equals"的用法 Java: String str1 = "abc&quo ...

  8. 编译安装http2.4

    编译安装http2.4 1.安装相关依赖包 [root@centos7 ~]yum -y install gcc make 2.下载http2.4包,并解压 [root@centos7 ~]#tar ...

  9. Byobu安装与使用

    机子为Ubuntu18 Byobu安装 sudo apt-get install byobu Byobu安装后默认禁用,需要启用Byobu,之后每次登陆自动启用Byobu byobu-enable 还 ...

  10. 虫师Selenium2+Python_00学习大纲

        1.自动化测试基础 5.自动化测试模型 9.Selenium Grid2 13.GitHub托管项目 2.测试环境搭建 6.SeleniumIDE 10.Python多线程 14.持续集成Je ...