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

发布于:2024-04-25 ⋅ 阅读:(21) ⋅ 点赞:(0)

七、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_ingredient

import (
	"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_ingredient

import "fmt"

// 饮料装饰器: 加配料的饮料
type drinkWithIngredient interface {
	// 饮料的价格
	cost() int
}

// 加糖饮料
type drinkWithSugar struct {
	d drinkWithIngredient
}

func (d *drinkWithSugar) cost() int {
	c := 3
	total := d.d.cost() + c
	fmt.Printf("加 %v 元的糖后, 共 %v 元. ", c, total)
	return total
}

// 加冰饮料
type drinkWithIce struct {
	d drinkWithIngredient
}

func (d *drinkWithIce) cost() int {
	c := 1
	total := d.d.cost() + c
	fmt.Printf("加 %v 元的冰后, 共 %v 元. ", c, total)
	return total
}

7.1.3 drink.go

package _71drink_ingredient

import "fmt"

// 饮料
type drink interface {
	// 饮料的价格
	cost() int
}

// 奶茶
type milkTea struct{}

func (t *milkTea) cost() int {
	c := 20
	fmt.Printf("一杯奶茶 %v 元\n", c)
	return c
}

// 绿茶
type greenTea struct{}

func (t *greenTea) cost() int {
	c := 10
	fmt.Printf("一杯绿茶 %v 元\n", c)
	return c
}

// 柠檬茶
type lemonTea struct{}

func (t *lemonTea) cost() int {
	c := 5
	fmt.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 notifierDecorator
	n = &qqNotifierDecorator{}
	n = &wechatNotifierDecorator{wrappee: n}
	n = &phoneNotifierDecorator{wrappee: n}
	n.notify()
}

7.2.2 notifier_decorator

package _72notifier

import "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 _72notifier

type notifier interface {
	notify()
}

7.3 idraw

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

7.3.1 idraw_test

package _73idraw

import (
	"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 _73idraw

type 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 _73idraw

import "fmt"

type ColorDraw struct {
	iDraw IDraw
	color 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 _73idraw

import "fmt"

type FontDraw struct {
	iDraw IDraw
	font  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)
}

网站公告

今日签到

点亮在社区的每一天
去签到