【设计模式】7、decorate 装饰模式

文章目录

  • 七、decorate 装饰模式
    • 7.1 饮料:类型+配料
      • 7.1.1 drink_with_ingredient_test.go
      • 7.1.2 drink_with_ingredient.go
      • 7.1.3 drink.go
    • 7.2 notifier
      • 7.2.1 notifier_decorator_test
      • 7.2.2 notifier_decorator
      • 7.2.3 notifier
    • 7.3 idraw
      • 7.3.1 idraw_test
      • 7.3.2 idraw
      • 7.3.3 color_draw.go
      • 7.3.4 font_draw.go

七、decorate 装饰模式

装饰设计(装饰者模式 / 装饰器模式)

如果希望增强行为,可以使用 decorate 模式。且支持嵌套多层(套娃)。

和 proxy 模式的区别:proxy 只套了一层,而且行为相同。而 decorate 可以套多层,且行为会增强。

以具体例子,会更容易理解。

7.1 饮料:类型+配料

参考:https://www.bilibili.com/video/BV1Vp4y187dK/?spm_id_from=333.337.search-card.all.click&vd_source=5dfe92471f5072eaffbf480e25acd82d

有多种饮料类型:如奶茶、绿茶、柠檬茶。

如果希望在此基础上,添加配料:如布丁、珍珠、糖。

则可以定义【配料装饰者 interface】,并使 布丁、珍珠分别实现该 interface。

那么就可以套娃了,比如:

奶茶 = New奶茶()1份布丁的奶茶 = New布丁(奶茶)2份布丁的奶茶 = New布丁(New布丁(奶茶))2份布丁, 且加3份珍珠的奶茶 = New珍珠(New珍珠(New珍珠(New布丁(New布丁(奶茶)))))

上文的配料就是装饰:因为各种配料都实现了同样的 interface,所以可以层层嵌套。每层都“增强”了行为。

目录层级如下:

07decorator/071drink_ingredient
├── drink.go
├── drink_with_ingredient.go
└── drink_with_ingredient_test.go

7.1.1 drink_with_ingredient_test.go

package _71drink_ingredientimport ("github.com/stretchr/testify/require""testing"
)/*
=== RUN   TestDrinkWithIngredient
一杯奶茶 20 元一杯奶茶 20 元
加 3 元的糖后, 共 23 元.一杯奶茶 20 元
加 3 元的糖后, 共 23 元. 加 3 元的糖后, 共 26 元.一杯奶茶 20 元
加 3 元的糖后, 共 23 元. 加 3 元的糖后, 共 26 元. 加 1 元的冰后, 共 27 元.--- PASS: TestDrinkWithIngredient (0.00s)
PASS
*/
func TestDrinkWithIngredient(t *testing.T) {m := &milkTea{}require.EqualValues(t, 20, m.cost())t.Log()ms := &drinkWithSugar{m}require.EqualValues(t, 20+3, ms.cost())t.Log()mss := &drinkWithSugar{ms}require.EqualValues(t, 20+3+3, mss.cost())t.Log()mssi := &drinkWithIce{mss}require.EqualValues(t, 20+3+3+1, mssi.cost())
}/*
=== RUN   TestDrinkWithIngredientByInterface
一杯奶茶 20 元
加 3 元的糖后, 共 23 元. 加 3 元的糖后, 共 26 元. 加 1 元的冰后, 共 27 元.
--- PASS: TestDrinkWithIngredientByInterface (0.00s)
PASS
*/
func TestDrinkWithIngredientByInterface(t *testing.T) {var d drinkWithIngredient = &milkTea{}for i := 0; i < 2; i++ {d = &drinkWithSugar{d}}d = &drinkWithIce{d}require.EqualValues(t, 20+3+3+1, d.cost())
}

7.1.2 drink_with_ingredient.go

package _71drink_ingredientimport "fmt"// 饮料装饰器: 加配料的饮料
type drinkWithIngredient interface {// 饮料的价格cost() int
}// 加糖饮料
type drinkWithSugar struct {d drinkWithIngredient
}func (d *drinkWithSugar) cost() int {c := 3total := d.d.cost() + cfmt.Printf("加 %v 元的糖后, 共 %v 元. ", c, total)return total
}// 加冰饮料
type drinkWithIce struct {d drinkWithIngredient
}func (d *drinkWithIce) cost() int {c := 1total := d.d.cost() + cfmt.Printf("加 %v 元的冰后, 共 %v 元. ", c, total)return total
}

7.1.3 drink.go

package _71drink_ingredientimport "fmt"// 饮料
type drink interface {// 饮料的价格cost() int
}// 奶茶
type milkTea struct{}func (t *milkTea) cost() int {c := 20fmt.Printf("一杯奶茶 %v 元\n", c)return c
}// 绿茶
type greenTea struct{}func (t *greenTea) cost() int {c := 10fmt.Printf("一杯绿茶 %v 元\n", c)return c
}// 柠檬茶
type lemonTea struct{}func (t *lemonTea) cost() int {c := 5fmt.Printf("一杯柠檬茶 %v 元\n", c)return c
}

7.2 notifier

07decorator/072notifier
├── notifier.go
├── notifier_decorator.go
└── notifier_decorator_test.go

7.2.1 notifier_decorator_test

/*
=== RUN   TestNotifierDecorator
qq发送消息
wechat发送消息
phone发送消息
--- PASS: TestNotifierDecorator (0.00s)
PASS
*/
func TestNotifierDecorator(t *testing.T) {var n notifierDecoratorn = &qqNotifierDecorator{}n = &wechatNotifierDecorator{wrappee: n}n = &phoneNotifierDecorator{wrappee: n}n.notify()
}

7.2.2 notifier_decorator

package _72notifierimport "fmt"type notifierDecorator interface {notifier
}type qqNotifierDecorator struct {wrappee notifierDecorator
}func (n *qqNotifierDecorator) notify() {if n.wrappee != nil {n.wrappee.notify()}fmt.Println("qq发送消息")
}type wechatNotifierDecorator struct {wrappee notifierDecorator
}func (n *wechatNotifierDecorator) notify() {if n.wrappee != nil {n.wrappee.notify()}fmt.Println("wechat发送消息")
}type phoneNotifierDecorator struct {wrappee notifierDecorator
}func (n *phoneNotifierDecorator) notify() {if n.wrappee != nil {n.wrappee.notify()}fmt.Println("phone发送消息")
}

7.2.3 notifier

package _72notifiertype notifier interface {notify()
}

7.3 idraw

├── color_draw.go
├── font_draw.go
├── idraw.go
└── idraw_test.go

7.3.1 idraw_test

package _73idrawimport ("github.com/stretchr/testify/require""testing"
)func TestIDraw(t *testing.T) {var d IDraw = &Square{}d = NewColorDraw(d, "black")d = NewFontDraw(d, "12px")require.EqualValues(t, "正方形, 颜色是: black, 字体是: 12px", d.Draw())d = &Circle{}d = NewColorDraw(NewFontDraw(d, "14px"), "blue")require.EqualValues(t, "圆形, 字体是: 14px, 颜色是: blue", d.Draw())
}

7.3.2 idraw

package _73idrawtype IDraw interface {Draw() string
}type Square struct{}func (d *Square) Draw() string {return "正方形"
}type Circle struct{}func (d *Circle) Draw() string {return "圆形"
}

7.3.3 color_draw.go

package _73idrawimport "fmt"type ColorDraw struct {iDraw IDrawcolor string
}func NewColorDraw(d IDraw, c string) IDraw {return &ColorDraw{iDraw: d, color: c}
}func (d *ColorDraw) Draw() string {return fmt.Sprintf("%v, 颜色是: %v", d.iDraw.Draw(), d.color)
}

7.3.4 font_draw.go

package _73idrawimport "fmt"type FontDraw struct {iDraw IDrawfont  string
}func NewFontDraw(d IDraw, c string) IDraw {return &FontDraw{iDraw: d, font: c}
}func (d *FontDraw) Draw() string {return fmt.Sprintf("%v, 字体是: %v", d.iDraw.Draw(), d.font)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/1394.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【人工智能基础】经典逻辑与归结原理

本章节的大部分内容与离散数学的命题、谓词两章重合。 假言推理的合式公式形式 R,R→P⇒PR,R∨P⇒P 链式推理 R→P,P→Q⇒R→QR∨P,P∨Q⇒R∨Q 互补文字&#xff1a;P和P 亲本子句&#xff1a;含有互补文字的子句 R∨P,P∨Q为亲本子句 注意&#xff1a; 必须化成析取范式…

命理八字之电子木鱼的代码实现

#uniapp# #电子木鱼# 不讲废话&#xff0c;上截图 目录结构如下图 功能描述&#xff1a; 点击一下&#xff0c;敲一下&#xff0c;伴随敲击声&#xff0c;可自动点击。自动点击需看视频广告&#xff0c;或者升级VIP会员。 疑点解答&#xff1a; 即animation动画的时候&…

Window中Jenkins部署asp/net core web主要配置

代码如下 D: cd D:\tempjenkins\src\ --git工作目录 dotnet restore -s "https://nuget.cdn.azure.cn/v3/index.json" --nuget dotnet build dotnet publish -c release -o %publishPath% --发布路径

装饰器学习

【一】什么是装饰器 装饰器指的就是为被装饰的对象添加新的功能 器 代表工具 增加后的调用方式不变 在不改变源代码和调用方式的基础上增加额外的新功能 【二】装饰器的用途 对上线后的程序进行新功能的增加和修改 给一个功能增加新的需求或者改变原来的程序运行逻辑 【…

Day08React——第八天

useEffect 概念&#xff1a;useEffect 是一个 React Hook 函数&#xff0c;用于在React组件中创建不是由事件引起而是由渲染本身引起的操作&#xff0c;比如发送AJAx请求&#xff0c;更改daom等等 需求&#xff1a;在组件渲染完毕后&#xff0c;立刻从服务器获取频道列表数据…

iOS RACScheduler 使用详解

RACScheduler 是 ReactiveCocoa 框架中的一个关键组件&#xff0c;用于在 iOS 开发中管理任务的并发执行。以下是如何详细使用 RACScheduler 的指南&#xff0c;以 Markdown 格式展示。 主要调度器 主线程调度器 用于在主线程上执行任务&#xff0c;通常用于 UI 更新操作。 …

Java:二叉树(1)

从现在开始&#xff0c;我们进入二叉树的学习&#xff0c;二叉树是数据结构的重点部分&#xff0c;在了解这个结构之前&#xff0c;我们先来了解一下什么是树型结构吧&#xff01; 一、树型结构 1、树型结构简介 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>…

通用类的中文实体命名识别

中文命名实体识别&#xff08;Chinese NER&#xff09;调研&#xff1a;https://github.com/taishan1994/awesome-chinese-ner 论文&#xff1a;Unified Structure Generation for Universal Information Extraction &#xff08;一统实体识别、关系抽取、事件抽取、情感分析&a…

TCP协议学习记录

TCP协议学习记录 简述 对TCP有诸多疑惑的地方&#xff1a; 1、TCP和socket的关系 2、TCP客户端和服务端如何区分 3、TCP连接的两端&#xff0c;端口号需要一致吗 什么是socket 一种编程抽象 编写程序时&#xff0c;利用socket可以使用TCP&#xff1b;假设现在已经将TCP协议…

Matlab无基础快速上手1(遗传算法框架)

本文用经典遗传算法框架模板&#xff0c;对matlab新手友好&#xff0c;快速上手看懂matlab代码&#xff0c;快速应用实践&#xff0c;源代码在文末给出。 基本原理&#xff1a; 遗传算法&#xff08;Genetic Algorithm&#xff0c;GA&#xff09;是一种受生物学启发的优化算法…

在Gtiee搭建仓库传代码/多人开发/个人代码备份---git同步---TortoiseGit+TortoiseSVN

文章目录 前言1.安装必要软件2. Gitee建立新仓库git同步2.1 Gitee建立新仓库2.2 Gitee仓库基本配置2.3 Git方式进行同步 3. TortoiseGitTortoiseSVN常用开发方式3.1 秘钥相关3.2 TortoiseGit拉取代码TortoiseGit提交代码 4. 其他功能探索总结 前言 正常企业的大型项目都会使用…

TR5 - Transformer的位置编码

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 目录 前言什么是位置编码1. 定义2. 三角函数3. 位置编码公式4. 位置编码示例 可视化理解位置编码1. 代码实现2. 观察不同位置对应的曲线3. 整句话的位置编码可…

排序 “贰” 之选择排序

目录 ​编辑 1. 选择排序基本思想 2. 直接选择排序 2.1 实现步骤 2.2 代码示例 2.3 直接选择排序的特性总结 3. 堆排序 3.1 实现步骤 3.2 代码示例 3.3 堆排序的特性总结 1. 选择排序基本思想 每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个…

Guitar Pro简谱输入方法 Guitar Pro简谱音高怎么调整,Guitar Pro功能介绍

一、新版本特性概览 Guitar Pro v8.1.1 Build 17在保留了前版本强大功能的基础上&#xff0c;进一步优化了用户体验和功能性能。新版本主要更新包括以下几个方面&#xff1a; 界面优化&#xff1a;新版界面更加简洁美观&#xff0c;操作更加便捷&#xff0c;即使是初学者也能快…

aixcoder介绍

最近了解了一下国内开源的aixcoder&#xff0c;代码自动补全的工具&#xff0c;可以理解为国内版开源的的copilot。 一开始公司给提供了一个代码自动补全的插件&#xff0c;这几天发现原来用的就是aixcoder&#xff0c;所以专门了解了一下。。 介绍 aiXcoder 是一款基于深度…

在线拍卖系统,基于SpringBoot+Vue+MySql开发的在线拍卖系统设计和实现

目录 一. 系统介绍 二. 功能模块 2.1. 管理员功能模块 2.2. 用户功能模块 2.3. 前台首页功能模块 2.4. 部分代码实现 一. 系统介绍 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系…

Docker - 简介

原文地址&#xff0c;使用效果更佳&#xff01; Docker - 简介 | CoderMast编程桅杆https://www.codermast.com/dev-tools/docker/docker-introduce.html Docker是什么&#xff1f; Docker 是一个开源的应用容器引擎&#xff0c;基于 Go 语言 并遵从 Apache2.0 协议开源。 D…

vulfocus靶场couchdb 权限绕过 (CVE-2017-12635)

Apache CouchDB是一个开源数据库&#xff0c;专注于易用性和成为"完全拥抱web的数据库"。它是一个使用JSON作为存储格式&#xff0c;JavaScript作为查询语言&#xff0c;MapReduce和HTTP作为API的NoSQL数据库。应用广泛&#xff0c;如BBC用在其动态内容展示平台&…

串口RS485

1.原理 全双工&#xff1a;在同一时刻可以同时进行数据的接收和数据的发送&#xff0c;两者互不影响 半双工&#xff1a;在同一时刻只能进行数据的接收或者数据的发送&#xff0c;两者不能同时进行 差分信号幅值相同&#xff0c;相位相反&#xff0c;有更强的抗干扰能力。 干…

vlan的学习笔记1

vlan&#xff1a; 1.一般情况下:以下概念意思等同: 一个vlan一个广播域 一个网段 一个子网 2.一般情况下: &#xff08;1&#xff09;相同vlan之间可以直接通信&#xff0c;不同vlan之间不能直接通信! &#xff08;2&#xff09;vlan技术属于二层技术&…