我的设计模式之旅、05 装饰模式

编程旅途是漫长遥远的,在不同时刻有不同的感悟,本文会一直更新下去。

程序介绍

image-20220911031259891

本程序实现装饰模式。小明和小王去吃沙县小吃,各自喜欢不同的搭配,需要考虑每个人饮食喜好不同,随时可能的变化。

小明想吃不带汤的面
拿个碗装了面条加个炸蛋加牛肉片加点酱汁
小王想吃馄饨,还特别爱吃鸡腿
拿个碗装了馄饨加个鸡腿加个鸡腿加点汤

程序代码

decorator.go

package main

import "fmt"

type IComponent interface {
	operation()
}

type Noodle struct{}
type Huntun struct{}

func (e Noodle) operation() {
	fmt.Printf("拿个碗装了面条")
}
func (e Huntun) operation() {
	fmt.Printf("拿个碗装了馄饨")
}

type Decorator struct {
	component IComponent
}

func (d *Decorator) setComponent(c IComponent) {
	d.component = c
}

func (d *Decorator) operation() {
	if d.component != nil {
		d.component.operation()
	}
}

type Egg struct{ Decorator }
type Beef struct{ Decorator }
type Sauce struct{ Decorator }
type Soup struct{ Decorator }
type Chicken struct{ Decorator }

func (e Egg) operation() {
	e.Decorator.operation()
	fmt.Printf("加个炸蛋") // 可以是该结构类型独有的方法
}

func (e Beef) operation() {
	e.component.operation()
	fmt.Printf("加牛肉片")
}

func (e Sauce) operation() {
	e.component.operation()
	fmt.Printf("加点酱汁")
}

func (e Soup) operation() {
	e.component.operation()
	fmt.Printf("加点汤")
}

func (e Chicken) operation() {
	e.component.operation()
	fmt.Printf("加个鸡腿")
}

main.go

package main

import "fmt"

func main() {
	fmt.Println("小明想吃不带汤的面")
	noodle := Noodle{}
	egg := Egg{Decorator{noodle}}
	beef := Beef{Decorator{egg}}
	sauce := Sauce{Decorator{beef}}
	sauce.operation()
	/* -------------------------------------------------------------------------- */
	fmt.Println("\n小王想吃馄饨,还特别爱吃鸡腿")
	huntun := Huntun{}
	chicken1 := Chicken{}
	chicken2 := Chicken{}
	soup := Soup{}
	chicken1.setComponent(huntun)
	chicken2.setComponent(chicken1)
	soup.setComponent(chicken2)
	soup.operation()
}

Console

小明想吃不带汤的面
拿个碗装了面条加个炸蛋加牛肉片加点酱汁
小王想吃馄饨,还特别爱吃鸡腿
拿个碗装了馄饨加个鸡腿加个鸡腿加点汤

思考总结

什么是装饰模式

装饰器模式(Decorator Pattern)允许向一个现有的对象添加新的功能,同时又不改变其结构。这种类型的设计模式属于结构型模式,它是作为现有的类的一个包装。

image-20220909234039141

装饰模式:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。

面对变化,如果采用生成子类的方式进行扩充,为支持每一种扩展的组合,会产生大量的子类。事实上,这些子类多半只是为某个对象增加一些职责,此时通过装饰模式,可以更加灵活、动态、透明的方式给单个对象添加职责,在不需要时也可以撤销相应职责。这种模式创建了一个装饰类,用来包装原有的类,并在保持类方法签名完整性的前提下,提供了额外的功能。

主要解决:一般的,我们为了扩展一个类经常使用继承方式实现,由于继承为类引入静态特征,并且随着扩展功能的增多,子类会很膨胀。

何时使用:在不想增加很多子类的情况下扩展类。

如何解决:将具体功能职责划分,同时继承装饰者模式。

关键代码: 1、Component 类充当抽象角色,不应该具体实现。 2、修饰类引用和继承 Component 类,具体扩展类重写父类方法。

应用实例: 1、孙悟空有 72 变,当他变成"庙宇"后,他的根本还是一只猴子,但是他又有了庙宇的功能。 2、不论一幅画有没有画框都可以挂在墙上,但是通常都是有画框的,并且实际上是画框被挂在墙上。在挂在墙上之前,画可以被蒙上玻璃,装到框子里;这时画、玻璃和画框形成了一个物体。

优点:装饰类和被装饰类可以独立发展(类的核心职责和装饰功能相互分开),不会相互耦合,装饰模式是继承的一个替代模式,装饰模式可以动态扩展一个实现类的功能。把类中的装饰功能从类中搬移去除,这样简化原来的类。

缺点:多层装饰比较复杂。

使用场景: 1、扩展一个类的功能。 2、动态增加功能,动态撤销。

注意事项:可代替继承。当系统需要新功能的时候,是向旧的类中添加新的代码。这些新加的代码通常装饰了原有类的核心职责或主要行为。由于在主类或子类中加入了新的字段、新的方法、新的逻辑,从而增加了主类的复杂度。而加入这些东西仅仅是为了满足一些只在某种特定情况下才会执行的特殊行为。

灵活变通:如果只有一个ConcreteComponent类没有抽象的Component类,那么Decorator类可以是ConreteComponent的一个子类。如果只有一个ConcreteDecorator类,那么就没有必要单独建立一个Decorator类,可以把DecoratorConcreateDecorator的责任合并成一个类。

与建造者模式不同点:建造者模式要求建造过程必须是稳定的,而装饰模式可以是不固定过程,可以组合出无数种方案。

参考资料

  • 《Go语言核心编程》李文塔
  • 《Go语言高级编程》柴树彬、曹春辉
  • 《大话设计模式》程杰
  • 单例模式 | 菜鸟教程
posted @ 2022-09-10 00:23  小能日记  阅读(22)  评论(0编辑  收藏  举报