结构型设计模式—装饰器模式

2024-01-01 09:36:30

装饰器模式:不改变原对象的基础上,灵活地给对象添加额外职责。装饰器相比于生成子类更加灵活。即将实现同一接口的父类当做参数传入包装器对象,动态创建出新的对象。

给对象添加新行为最简单直观的办法就是扩展本体对象,通过继承的方式达到目的。但使用继承有两个弊端:

  1. 继承是静态的,在编译期间就已经确定,无法在运行时改变对象的行为。
  2. 子类只能有一个父类,当需要添加的新功能太多时,易导致类的数量剧增。

使用装饰器模式,我们通过将现有对象放置在实现了相同一套接口的包装器对象中来动态地向现有对象添加新行为。在包装器中进行我们代码的扩展,有助于重用功能并且不会修改现有对象的代码,符合“开闭原则”。

装饰器模式中主要有如下几个角色:

  1. 客户端:会用多层装饰器来封装组件, 最后调用装饰好的包装器的方法,启动执行。
  2. 组件接口:Component声明装饰器对象和被装饰的组件对象要实现的公用接口。
  3. 组件实现:具体的组件实现类它的Operation方法中定义了组件的基础行为, 装饰类可以增强这些行为。
  4. 基础装饰类:拥有一个指向被封装对象的成员变量。 在自己的Operation方法中调用被装饰对象的Operation方法
  5. 具体装饰类:重写父类的Operation方法实现增强逻辑。类图里已经给出了要实现的主要逻辑,第四步的基础装饰类并不需要一定存在,完全可以由具体装饰类来持有对被装饰对象的引用,并实现增强逻辑,这样一来整体的结构会更简单一些。

去掉基础装饰类就是代理模式

定义一个游戏主机的产品接口,它就是上面类图中组件和装饰器的公共接口。

// PS5 产品接口
type PS5 interface {
 StartGPUEngine()
 GetPrice() int64
}

提供一个基础的产品实现类作为装饰器模式中的组件

// CD 版 PS5主机
type PS5WithCD struct{}

func (p PS5WithCD) StartGPUEngine() {
 fmt.Println("start engine")
}
func (p PS5WithCD) GetPrice() int64 {
 return 5000
}

提供一个数字版游戏主机的实现作为组件实现类。

// PS5 数字版主机
type PS5WithDigital struct{}

func (p PS5WithDigital) StartGPUEngine() {
 fmt.Println("start normal gpu engine")
}

func (p PS5WithDigital) GetPrice() int64 {
 return 3600
}

用两个装饰器实现的Plus版和主题配色版的两个增强。

// Plus 版的装饰器
func (p *PS5MachinePlus) SetPS5Machine(ps5 PS5) {
 p.ps5Machine = ps5
}

func (p PS5MachinePlus) StartGPUEngine() {
 p.ps5Machine.StartGPUEngine()
 fmt.Println("start plus plugin")
}

func (p PS5MachinePlus) GetPrice() int64 {
 return p.ps5Machine.GetPrice() + 500
}

// 主题色版的装饰器
type PS5WithTopicColor struct {
 ps5Machine PS5
}

func (p *PS5WithTopicColor) SetPS5Machine(ps5 PS5) {
 p.ps5Machine = ps5
}

func (p PS5WithTopicColor) StartGPUEngine() {
 p.ps5Machine.StartGPUEngine()
 fmt.Println("尊贵的主题色主机,GPU启动")
}

func (p PS5WithTopicColor) GetPrice() int64 {
 return p.ps5Machine.GetPrice() + 200
}

两个增强还可以叠加在一起,组合出即高配主题限定版主机.

func main() {
 ps5MachinePlus := PS5MachinePlus{}
 ps5MachinePlus.SetPS5Machine(PS5WithCD{})
 // ps5MachinePlus.SetPS5Machine(PS5WithDigital{}) // 可以在更换主机
 ps5MachinePlus.StartGPUEngine()
 price := ps5MachinePlus.GetPrice()
 fmt.Printf("PS5 CD 豪华Plus版,价格: %d 元\n\n", price)

 ps5WithTopicColor := PS5WithTopicColor{}
 ps5WithTopicColor.SetPS5Machine(ps5MachinePlus)
 ps5WithTopicColor.StartGPUEngine()
 price = ps5WithTopicColor.GetPrice()
 fmt.Printf("PS5 CD 豪华Plus 经典主题配色版,价格: %d 元\n", price)
}

装饰器和代理在结构上类似,在行为上跟职责链模式类似

  • 装饰器模式强调自身功能的扩展。
  • 代理模式强调对代理过程的控制。

但装饰器的使用必将会给程序带来更高的复杂性,更低的可读性,子类集成的代码结构会更直白易懂一些,而且虽然装饰器符合“开闭原则”,但是它会给程序带来更多的类,动态装饰在多层装饰时会更复杂,对于大型项目来说也是更好维护,我最近写一个项目就是,实现原型后要对基础Entry类进行更加具体的拓展,这无疑要对代码进行重构,后续可能进一步升级项目功能,那就需要不断重构,使用装饰器模式可以使代码健壮性良好,有利于进一步维护。

参考公众号网管叨bi叨

文章来源:https://blog.csdn.net/zhaicheng55/article/details/135322264
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。