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

devtools/2024/9/19 18:40:27/ 标签: 设计模式

文章目录

  • 七、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.ppmy.cn/devtools/13963.html

相关文章

《架构师启示录》三大层次学习企业架构框架TOGAF

对于一名架构师来讲&#xff0c;如果说编程语言是知识库层次中的入门石&#xff0c;那么企业架构框架则相当于知识库层次中的金字塔尖。如果想成长为企业级的架构师&#xff0c;企业架构框架是必须要攀登的高塔。 目前国内绝大多数企业采用TOGAF标准&#xff0c;因此我们的讨论…

使用微信开发者工具模拟微信小程序定位

哈喽&#xff0c;各位同僚们&#xff0c;我们平时在测试微信小程序的时候&#xff0c;如果小程序中有获取定位或者地图的功能&#xff0c;测试场景中常常需要去模拟不同的位置&#xff0c;例如我们模拟在电子围栏的外面、里面和边界区域等。那么&#xff0c;我们如何在模拟微信…

基于Springboot的职称评审管理系统

基于SpringbootVue的职称评审管理系统的设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 首页 评审条件 论坛信息 系统公告 后台登录页面 用户管理 评审员管理 省份…

移动底盘激光里程计

参考链接&#xff1a;rf2o_laser_odometry - ROS Wiki srf_laser_odometry 移动机器人——里程计矫正 - 古月居

人工智能论文GPT-3(1):2020.5 Language Models are Few-Shot Learners;摘要;引言;scaling-law

摘要 近期的工作表明&#xff0c;在大量文本语料库上进行预训练&#xff0c;然后针对特定任务进行微调&#xff0c;可以在许多NLP任务和基准测试中取得实质性进展。虽然这种方法在架构上通常是与任务无关的&#xff0c;但仍然需要包含数千或数万示例的针对特定任务的微调数据集…

刷题之Leetcode19题(超级详细)

19.删除链表的倒数第N个节点 力扣题目链接(opens new window)https://leetcode.cn/problems/remove-nth-node-from-end-of-list/ 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 进阶&#xff1a;你能尝试使用一趟扫描实现吗&#x…

webpack

1、现代web开发的问题&#xff1a; 1&#xff09;采用模块化开发&#xff0c;但是不同的浏览器对于模块化的支持不一样&#xff0c;模块化本身又存在多种规范 2&#xff09;使用新特性例如es或者ts来编写代码&#xff0c;提高效率保证安全性&#xff0c;也会用sass,less来编写…

Ubuntu Pycharm安装

下载PyCharm&#xff0c;https://www.jetbrains.com/pycharm/download/?sectionlinux 然后按照下图执行安装&#xff1a; 安装的时候可能出现的问题&#xff1a; 问题1&#xff1a;No JDK found. Please validate either PYCHARM_JDK, JDK_HOME or JAVA_HOME environment var…

【计算机考研】计算机行业考研还有性价比吗?

刚上大学的时候觉得本科毕业就够了 到了大三找工作才发现自己什么都不会 只能踏上考研路 目前研一在读&#xff0c;身边有很多工作了几年又回来读研的同学&#xff0c;只能说现在计算机行业研究生还是比本科生有竞争力的 如果你的本科和我一样没有学什么技术&#xff0c;那…

【论文阅读】互连网络的负载平衡路由算法 (RLB RLBth)

前言Oblivious Load Balancing 不经意路由负载平衡 1. oblivious routing 不经意/无关路由的背景知识 1. oblivious routing, adaptive routing & minimal/non-minimal routing algorithms 2. Balancing a 1-Dimensional ring: RLB and RLBth 一维 ring 的 RLB and RLBth 1…

form-serialize插件,快速收集表单元素的值

form-serialize插件可以快速获得表单元素的值&#xff0c;主要用于当表单很多的情况下&#xff0c;将表单的值一起打包发给服务器。 使用方法&#xff1a; 1.引入插件 2.获取表单的dom 3.使用插件的serialize方法 serialize方法有两个参数&#xff0c;第一个是获取到的表单d…

SpringMvc(2)RequestMapping注解

RequestMapping注解 1 、RequestMapping的作用2、RequestMapping的出现位置3、类上与方法上结合使用4、RequestMapping注解的value属性4.1 value属性的使用4.2 Ant风格的value4.3 value中的占位符&#xff08;重点&#xff09; 5、RequestMapping注解的method属性5.2衍生Mappin…

九:深入理解 CountDownLatch —— 闭锁/倒计时锁

目录 1、背景2、CountDownLatch 入门2.1、概念2.2、案例 3、CountDownLatch 源码分析3.1、类结构3.2、await() 方法 —— CountDownLatch3.2.1、acquireSharedInterruptibly() 方法 —— AQS3.2.1.1、tryAcquireShared() 方法 —— CountDownLatch.Sync3.2.1.2、doAcquireShare…

Mysql 锁学习笔记

目录 Innodb锁 共享锁与排它锁 锁兼容级别 意向锁 - 表级锁 代码示例 表级锁类型兼容性 行锁 代码示例 间隙锁 代码示例 临键锁 - 行锁加间隙锁 插入意向锁 自增锁 SELECT的加锁规则 (RR) 查看锁状态命令 3.0 前置条件 3.1 主键检索 3.2 唯一索引检索 3.3 普…

233 基于matlab的多通道非负矩阵分解(MNMF)算法

基于matlab的多通道非负矩阵分解&#xff08;MNMF&#xff09;算法。其能够寻找到一个非负矩阵W和一个非负矩阵H&#xff0c;满足条件VW*H,从而将一个非负的矩阵分解为左右两个非负矩阵的乘积。使用EM准则对混合信号进行分解。程序已调通&#xff0c;可直接运行。 233 多通道非…

ruoyi-nbcio-plus基于vue3的flowable的websocket消息组件的升级修改(一)

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 http://122.227.135.243:9666/ 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a…

Java之多态

一、多态前言 1.为什么要使用多态 Java中使用多态的主要目的是提高代码的可重用性和扩展性&#xff0c;使得代码更加灵活和易于维护。通过多态&#xff0c;我们可以将不同的对象看做是同一种类型&#xff0c;从而使得我们可以使用同一种接口来操作这些对象&#xff0c;而不必…

如何使用自定义Promptbooks优化您的安全工作流程

在当今的数字化时代&#xff0c;安全工作流程的优化变得前所未有的重要。安全团队需要快速、有效地响应安全事件&#xff0c;以保护组织的数据和资产。Microsoft Copilot for Security提供了一种强大的工具——自定义Promptbooks&#xff0c;它可以帮助安全专家通过自动化和定制…

springboot项目整合kafka实现消息队列

一、Docker镜像、容器准备&#xff1a; 1.1拉取镜像&#xff1a; 前提是虚拟机安装了docker&#xff0c;可以自行看其他文章进行安装 docker pull ubuntu/kafka docker pull zookeeper1.2运行容器 先启动zookeeper容器&#xff0c;因为kafka依赖于zookeeper docker run -d …

【数据仓库工具箱】DW/BI系统的核心元素和基本要求

核心元素 DW/BI 环境划分为4个不同的&#xff0c;各具特色的组成部分。分别是&#xff1a;操作型源数据&#xff0c;ETL系统&#xff0c;数据展现和商业智能应用。 操作型源数据 记录的是操作型系统&#xff0c;用于获取业务事务。源数据关注的是处理性能和可用性。源系统一般…