Go设计模式(13)-装饰器模式

装饰器模式主要解决继承关系过于复杂的问题,通过组合来替代继承。它主要的作用是给原始类添加增强功能。 装饰器类和原始类继承同一个父类,这么看感觉和代理模式一样。但其实两者作用不同,装饰器模式主要用于增加功能,代理模式主要用于附加跟原始类无关的功能。

本文UML类图链接为:https://www.processon.com/view/link/609b39d6f346fb5a37705da6

本文代码链接为:https://github.com/shidawuhen/asap/blob/master/controller/design/13decorator.go

1.定义

1.1装饰器模式

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

UML类图:
在这里插入图片描述

1.2分析

首先我们需要理解,为什么组合优于继承?

  • 继承有诸多作用,但继承层次过深、过复杂,会影响到代码的可维护性。
  • 继承主要有三个作用:表示is-a关系,支持多态特性,代码复用。而这三个作用都可以通过组合、接口、委托三个技术手段来达成。除此之外,利用组合还能解决层次过深、过复杂的继承关系影响代码可维护性的问题。

再来看一下装饰器模式和代理模式的区别。
在这里插入图片描述

  • 从UML层面上,Proxy下没有子类,Decorator下却有子类。Proxy和Decorator有一部分作用是相同的,都需要包一下原始类的各个函数,只不过Proxy在包的过程中,会附加一些和原始类无关的功能。Proxy包原始类各个函数,主要是为了让ConcreteDecorator减少重复操作,只关注要增加功能的函数即可。
  • 从使用层面上说,Proxy很直接,客户端调用Proxy,Proxy调用原始类。装饰器模式则可以嵌套使用,A装饰B,B装饰C,C装饰D。

可能大家会问组合作用到底在哪?我们想一个场景,假设一个类Base有个功能封装的很好,但是有些地方想用的话,需要再加强一下,这个类是D1,如果使用继承的话,就需要进行重载、调用基类。如果别的地方想用D1,也还需要加强部分功能,这个类是D2,使用继承,则又需要重载、调用基类。这使得继承层次过深、过复杂了,然后继承是有必要的吗?仔细想一下不用继承、直接用组合,不但能实现目标,工作量减小,而且能去掉继承的束缚。

2.使用场景

装饰器模式一般用在基类功能封装不错,但使用的时候需要对功能进行一些加强,而这些加强版的功能也会被其它加强版需要,这种就比较适合。

尼古拉斯凯奇主演的《战争之王》不知道大家看过没有。记得里面有个场景,凯奇买了一架武装直升机,这时FBI待人抓捕,凯奇将直升机和导弹分开就合法了。直升机就是那个封装特别好的类,能够长距离飞行。想用武装直升机,就在上面加导弹。想用救援直升机就在上面加医生。想用武装救援直升机,就在上面即加导弹又加医生。

我们就按照这个例子写代码吧。

3.代码实现

package main

import "fmt"

/**
 * @Description: 飞行器接口,有fly函数
 */
type Aircraft interface {
   fly()
   landing()
}

/**
 * @Description: 直升机类,拥有正常飞行、降落功能
 */
type Helicopter struct {
}

func (h *Helicopter) fly() {
   fmt.Println("我是普通直升机")
}

func (h *Helicopter) landing() {
   fmt.Println("我有降落功能")
}

/**
 * @Description: 武装直升机
 */
type WeaponAircraft struct {
   Aircraft
}

/**
 * @Description: 给直升机增加武装功能
 * @receiver a
 */
func (a *WeaponAircraft) fly() {
   a.Aircraft.fly()
   fmt.Println("增加武装功能")
}

/**
 * @Description: 救援直升机
 */
type RescueAircraft struct {
   Aircraft
}

/**
 * @Description: 给直升机增加救援功能
 * @receiver r
 */
func (r *RescueAircraft) fly() {
   r.Aircraft.fly()
   fmt.Println("增加救援功能")
}

func main() {
   //普通直升机
   fmt.Println("------------普通直升机")
   helicopter := &Helicopter{}
   helicopter.fly()
   helicopter.landing()

   //武装直升机
   fmt.Println("------------武装直升机")
   weaponAircraft := &WeaponAircraft{
      Aircraft: helicopter,
   }
   weaponAircraft.fly()

   //救援直升机
   fmt.Println("------------救援直升机")
   rescueAircraft := &RescueAircraft{
      Aircraft: helicopter,
   }
   rescueAircraft.fly()

   //武装救援直升机
   fmt.Println("------------武装救援直升机")
   weaponRescueAircraft := &RescueAircraft{
      Aircraft: weaponAircraft,
   }
   weaponRescueAircraft.fly()
}

输出:

➜ myproject go run main.go

------------普通直升机

我是普通直升机

我有降落功能

------------武装直升机

我是普通直升机

增加武装功能

------------救援直升机

我是普通直升机

增加救援功能

------------武装救援直升机

我是普通直升机

增加武装功能

增加救援功能

代码实现中没有Decorator类,主要是因为Go组合的特性。之所以有Decorator,是因为Decorator中有component成员变量,Decorator中函数实现是调用component的函数,所以对于component中的每一个函数,Decorator都需要封装一下,否则无法使用。但是Go组合方式会自动完成这项任务,无需封装,自然也就不需要Decorator了。

总结

装饰器模式理解和使用都比较简单,主要通过组合方式实现复用能力,如果组合的变量为接口或者基类,便可实现串联功能。

在使用上,首先需要确定复用的功能抽象的比较好,以免使用的时候,发现很多增强功能可以收敛其中。其次判断是否有增强的功能需要串联的情况,如果有的话,使用装饰器模式是十分合适的。

装饰器模式体现了开闭原则、里氏替换原则、依赖倒转原则。

最后

大家如果喜欢我的文章,可以关注我的公众号(程序员麻辣烫)

我的个人博客为:https://shidawuhen.github.io/

往期文章回顾:

招聘

  1. 字节跳动|内推大放送
  2. 字节跳动|今日头条广州服务端研发工程师内推
  3. 字节跳动|抖音电商急招上海前端开发工程
  4. 字节跳动|抖音电商上海资深服务端开发工程师-交易
  5. 字节跳动|抖音电商武汉服务端(高级)开发工程师
  6. 字节跳动|飞书大客户产品经理内推咯
  7. 字节跳动|抖音电商服务端技术岗位虚位以待
  8. 字节跳动招聘专题

设计模式

  1. Go设计模式(12)-桥接模式
  2. Go设计模式(11)-代理模式
  3. Go设计模式(10)-原型模式
  4. Go设计模式(9)-建造者模式
  5. Go设计模式(8)-抽象工厂
  6. Go设计模式(7)-工厂模式
  7. Go设计模式(6)-单例模式
  8. Go设计模式(5)-类图符号表示法
  9. Go设计模式(4)-代码编写优化
  10. Go设计模式(4)-代码编写
  11. Go设计模式(3)-设计原则
  12. Go设计模式(2)-面向对象分析与设计
  13. Go设计模式(1)-语法

语言

  1. 一文搞懂pprof
  2. Go工具之generate
  3. Go单例实现方案
  4. Go通道实现原理
  5. Go定时器实现原理
  6. Beego框架使用
  7. Golang源码BUG追查
  8. Gin框架简洁版
  9. Gin源码剖析

架构

  1. 分页复选设计的坑
  2. 支付接入常规问题
  3. 限流实现2
  4. 秒杀系统
  5. 分布式系统与一致性协议
  6. 微服务之服务框架和注册中心
  7. 浅谈微服务
  8. 限流实现1
  9. CDN请求过程详解
  10. 常用缓存技巧
  11. 如何高效对接第三方支付
  12. 算法总结

存储

  1. MySQL开发规范
  2. Redis实现分布式锁
  3. 事务原子性、一致性、持久性的实现原理
  4. InnoDB锁与事务简析

网络

  1. HTTP2.0基础教程
  2. HTTPS配置实战
  3. HTTPS连接过程
  4. TCP性能优化

工具

  1. GoLand实用技巧
  2. 根据mysql表自动生成go struct
  3. Markdown编辑器推荐-typora

读书笔记

  1. 《毛选》推荐
  2. 原则
  3. 资治通鉴
  4. 敏捷革命
  5. 如何锻炼自己的记忆力
  6. 简单的逻辑学-读后感
  7. 热风-读后感
  8. 论语-读后感
  9. 孙子兵法-读后感

思考

  1. 为动员一切力量争取胜利而斗争
  2. 反对自由主义
  3. 实践论
  4. 评价自己的标准
  5. 服务端团队假期值班方案
  6. 项目流程管理
  7. 对项目管理的一些看法
  8. 对产品经理的一些思考
  9. 关于程序员职业发展的思考
  10. 关于代码review的思考
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值