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

server/2024/9/25 21:22:20/

文章目录

  • 七、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/server/14122.html

相关文章

如何在OpenWRT上配置SFTP远程文件传输

如何在OpenWRT上配置SFTP远程文件传输 OpenWRT 是一款广泛使用的开源路由器固件&#xff0c;它能够让普通的家用路由器具备高级路由功能&#xff0c;提供更多自定义和优化选项。本文将介绍如何在OpenWRT上配置SFTP&#xff08;SSH文件传输协议&#xff09;服务&#xff0c;以便…

Set实现(2)| LinkedHashSet

目录 1. LinkedHashSet的适用范围2. 源码分析与实例2.1 内部结构2.2 添加元素2.3 删除元素2.4 检查元素2.5 示例 3. 总结 在Java集合框架中&#xff0c; LinkedHashSet是一个用于存储不重复元素的有序集合。它实现了 Set接口&#xff0c;并提供了插入顺序的迭代访问。 Linke…

芒果YOLOv8改进组合161:动态标签分配ATSS+新颖轻量化非对称多级压缩LADH检测头组合改进,LADH作为原创可以发表SCI顶刊论文,小目标高效涨点

💡本篇内容:【芒果YOLOv8改进ATSS标签分配策略|第四集】芒果YOLOv8改进组合161:动态标签分配ATSS+新颖轻量化非对称多级压缩LADH检测头组合改进,小目标高效涨点 💡🚀🚀🚀本博客 标签分配策略ATSS改进+ 新颖轻量化非对称多级压缩LADH检测头组合改进,适用于 YOLOv…

抽象工厂模式设计实验

【实验内容】 楚锋软件公司欲开发一套界面皮肤库&#xff0c;可以对 Java 桌面软件进行界面美化。为了保护版权&#xff0c;该皮肤库源代码不打算公开&#xff0c;而只向用户提供已打包为 jar 文件的 class 字节码文件。用户在使用时可以通过菜单来选择皮肤&#xff0c;不同的…

QT Sqlite 内存模式 简单读写

//本文描述了QT Sqlite 内存模式 &#xff0c;使用QT 自带库文件&#xff0c;写入和读取。 //QT 6.2.4 MSVC2019调试通过。 //需要在pro文件中加入QT sql #include <QCoreApplication> #include <QSqlDatabase> #include <QSqlQuery> #include <QDebu…

简述PDF原理和实践

Hello&#xff0c;我是小恒不会java。 由于最近有输出PDF报表的项目需求&#xff0c;所以复习一下PDF到底是什么&#xff0c;该如何产生&#xff0c;如何应用至项目中。 更多参见Adobe官方文档&#xff08;https://www.adobe.com/cn/&#xff09; PDF原理 PDF&#xff08;Port…

python制作ppt

在Python中&#xff0c;你可以使用python-pptx库来创建和修改PowerPoint (.pptx) 文件。这个库允许你添加幻灯片、文本框、图片、形状、表格等元素&#xff0c;并可以调整它们的格式和布局。 下面是一个简单的例子&#xff0c;展示了如何使用python-pptx库来创建一个PPT文件&a…

使用 Rust 后,我​​使用 Python 的方式发生了变化

使用 Rust 后&#xff0c;我​​使用 Python 的方式发生了变化 Using type hints where possible, and sticking to the classic “make illegal state unrepresentable” principle. 尽可能使用类型提示&#xff0c;并坚持经典的“使非法状态不可表示”原则。 近年来&#xff…