golang设计模式——装饰器模式

2023-05-16

装饰器模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L749SF4Q-1660286893968)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220812113800453.png)]

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

UML类图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pCpQdKZm-1660286893969)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220812121958709.png)]

分析

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

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

再来看一下装饰器模式和代理模式的区别。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RnHQy0Wj-1660286893970)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220812123028389.png)]

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

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

使用场景

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

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

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

代码实现

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了。

实例

下面是一个简单的画画的例子,默认的 Square 只有基础的画画功能, ColorSquare 为他加上了颜色

代码

package decorator

// IDraw IDraw
type IDraw interface {
	Draw() string
}

// Square 正方形
type Square struct{}

// Draw Draw
func (s Square) Draw() string {
	return "this is a square"
}

// ColorSquare 有颜色的正方形
type ColorSquare struct {
	square IDraw
	color  string
}

// NewColorSquare NewColorSquare
func NewColorSquare(square IDraw, color string) ColorSquare {
	return ColorSquare{color: color, square: square}
}

// Draw Draw
func (c ColorSquare) Draw() string {
	return c.square.Draw() + ", color is " + c.color
}

单元测试

func TestColorSquare_Draw(t *testing.T) {
	sq := Square{}
	csq := NewColorSquare(sq, "red")
	got := csq.Draw()
	assert.Equal(t, "this is a square, color is red", got)
}

总结

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

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

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

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

golang设计模式——装饰器模式 的相关文章

  • 【设计模式】工厂模式(Factory Pattern)

    1 概述 工厂模式 Factory Pattern 是最常用的设计模式之一 它属于创建类型的设计模式 它提供了一种创建对象的最佳方式 在工厂模式中 我们在创建对象时不会对客户端暴露创建逻辑 并且是通过一个共同的接口来指向新创建的对象 工厂模
  • goland环境配置

    goland modules环境配置 下载和安装goland 环境配置 配置环境变量GOPATH 配置go modules GOPROXY代理的系统变量 工程目录中新建三个工作目录 goland中启用go modules 新建一个go程序
  • Go_接口、多态、接口继承、空接口、类型断言

    接口 接口是把所有具有共性的方法定义在一起 是方法集 任何类型实现了接口中所有的方法 就是实现了这个接口 接口可以实现多态 接口传递的是地址值 接口定义及调用 定义格式 tepe 接口名 interface 方法名 参数 返回值 调用格式1
  • Golang三剑客之Pflag、Viper、Cobra

    如何构建应用框架 想知道如何构建应用框架 首先你要明白 一个应用框架包含哪些部分 在我看来 一个应用框架需要包含以下 3 个部分 命令行参数解析 主要用来解析命令行参数 这些命令行参数可以影响命令的运行效果 配置文件解析 一个大型应用 通常
  • Go 程序编译过程(基于 Go1.21)

    版本说明 Go 1 21 官方文档 Go 语言官方文档详细阐述了 Go 语言编译器的具体执行过程 Go1 21 版本可以看这个 https github com golang go tree release branch go1 21 sr
  • 有哪些不错的 Golang 开源项目?

    目前人在字节做 Go 开发 寻找 Golang 开源项目学习目的可能是 想学习或者提高自己对 Go 项目的组织和编排能力 想学习 Go 项目的框架设计 想在一些 Go 语法上细节的优化和进阶 我推荐两个项目 一 tinode 这是一个开源的
  • 【go语言开发】Minio基本使用,包括环境搭建,接口封装和代码测试

    本文主要介绍go语言使用Minio对象存储 首先介绍搭建minio 创建bucket等 然后讲解封装minio客户端接口 包括但不限于 上传文件 下载 获取对象url 最后测试开发的接口 文章目录 前言 Minio docker安装mini
  • 【go语言开发】loglus日志框架的使用

    本文将简单介绍loglus框架的基本使用 并给出demo 文章目录 前言 Loglus常见用法 自定义日志级别 使用字段钩子 输出到多个位置 使用钩子实现自定义日志处理 demo
  • go-zero 开发入门-加法客服端示例

    定义 RPC 接口文件 接口文件 add proto 的内容如下 syntax proto3 package add 当 protoc gen go 版本大于 1 4 0 时需加上 go package 否则编译报错 unable to d
  • go-zero开发入门之网关往rpc服务传递数据2

    go zero 的网关服务实际是个 go zero 的 API 服务 也就是一个 http 服务 或者说 rest 服务 http 转 grpc 使用了开源的 grpcurl 库 当网关需要往 rpc 服务传递额外的数据 比如鉴权数据的时候
  • GoLong的学习之路,进阶,微服务之序列化协议,Protocol Buffers V3

    这章是接上一章 使用 RPC包 序列化中没有详细去讲 因为这一块需要看的和学习的地方很多 并且这一块是RPC中可以说是最重要的一块 也是性能的重要影响因子 今天这篇主要会讲其使用方式 文章目录 Protocol Buffers V3 背景以
  • GoLong的学习之路,进阶,Viper(yaml等配置文件的管理)

    本来有今天是继续接着上一章写微服务的 但是这几天有朋友说 再写Web框架的时候 遇到一个问题 就是很多的中间件 redis 微信 mysql mq 的配置信息写的太杂了 很不好管理 希望我能写一篇有管理配置文件的 所以这篇就放到今天写吧 微
  • go语言实现文件夹上传前后端代码案例

    go语言实现文件夹上传前后端代码案例 前端用于上传的测试界面 如果上传的文件夹有子文件要遍历子文件夹创建出子文件夹再进行拷贝 需要获取文件名和对应的路径 将文件的相对路径和文件对象添加到FormData中 这几行代码很关键 for let
  • 在AI技术的无情侵袭下,学学Java的23种设计模式还是非常有必要的

    目前国内80 程序员的主要工作是调用组合api实现各种业务需求 在顶层架构师设定好的框架下 做着重复且无聊的编码工作 如果未来ai被广泛应用 那么被替代的风险是很高的 比较扎心的是 其实目前用ai生成片段代码已经是各个公司比较普遍的做法了
  • Java设计模式:模板方法模式

    作者主页 欢迎来到我的技术博客 个人介绍 大家好 本人热衷于 Java后端开发 欢迎来交流学习哦 如果文章对您有帮助 记得 关注 点赞 收藏 评论 您的支持将是我创作的动力 让我们一起加油进步吧 文章目录 一 模板方法模式的定义 二 模板方
  • 设计模式(3)--对象结构(5)--外观

    1 意图 为子系统中的一组接口提供一个一致的界面 Facade模式定义了一个高层接口 这个接口使得 这一子系统更加容易使用 2 两种角色 子系统 Subsystem 外观 Facade 3 优点 3 1 对客户屏蔽了子系统组件 减少了客户处
  • 设计模式(三)-结构型模式(4)-组合模式

    一 为何需要组合模式 Composite 在代码设计中 有种情况是对象之间存在层次关系 即对象之间会存在父结点和子结点的关系 比如在文件管理系统中 所有文件和文件夹形成树状结构 文件夹目录里存在子文件夹和文件 文件夹属于枝结点 文件属于叶结
  • C++设计模式 #3策略模式(Strategy Method)

    动机 在软件构建过程中 某些对象使用的的算法可能多种多样 经常改变 如果将这些算法都写在类中 会使得类变得异常复杂 而且有时候支持不频繁使用的算法也是性能负担 如何在运行时根据需求透明地更改对象的算法 将算法和对象本身解耦 从而避免上述问题
  • go开发--操作mysql数据库

    在 Go 中访问 MySQL 数据库并进行读写操作通常需要使用第三方的 MySQL 驱动 Go 中常用的 MySQL 驱动有 github com go sql driver mysql 和 github com go xorm xorm
  • go cannot find package “github.com/gorilla/websocket“解读

    Go无法找到包 github com gorilla websocket 的解决方案 在Go开发过程中 我们经常会依赖第三方库来简化开发工作 而使用 go get 命令安装这些库时 有时候我们可能会遇到类似于以下错误的情况 plaintex

随机推荐

  • STM32 芯片锁死无法烧录问题解决

    芯片锁死原因 xff1a 1 烧进去的工程对应器件与目标器件不一致 xff1b 2 烧进去的工程HSE VALUE与目标板上晶振频率不一致 xff1b 3 将烧录引脚烧录 本人在使用F411时犯下了 比较愚蠢的错误 xff0c 因为PB3引
  • 【TPMS】 - 发射端2

    TPMS项目 发射端SP370 目录章节介绍 一 SP370数据手册浏览二 源码学习三 SP370的RF的部分详解四 RF数据包的发送和数据包格式解析1 目录 章节介绍 1 SP370数据手册浏览 浏览SP370的数据手册 xff0c 看一
  • 【TPMS】 -接收端1

    TPMS项目 接受端TDA5235 目录章节介绍 一 TPMS接收板概况介绍二 TDA5235的专业知识1三 寄存器配置工具 目录 章节介绍 1 TPMS接收板概况介绍 本节开始接收板部分的课程 xff0c 先对接收板的整体情况 xff0c
  • ST_link突然不能使用了

    一 发现问题 今天 xff0c 我使用st link烧写程序时突然不能用了 xff0c 我昨天都还可以正常使用 我用手摸仿真器时很热 我意识到可能坏了 二 解决过程 xff08 1 xff09 用keil5查看debug设置时就能找到ST
  • OpenCV 读取视频并保存为另一个视频

    测试代码如下 xff1a 功能 xff1a 读取视频 xff0c 缩小处理后再存为另一个视频 方法1 include lt opencv2 opencv hpp gt include lt opencv2 highgui highgui c
  • XML和JSON

    XML 简介 xml是什么 XML是一种可扩展标记语言 Extensible Markup Language xff0c 它是一种用于在计算机网络上进行数据传输的标准格式 XML使用标记来标识数据 xff0c 并且可以自定义标记 xff0c
  • STM32单片机蜂鸣器实验

    蜂鸣器可以分为两种 xff1a 有源蜂鸣器与无源蜂鸣器 xff0c 这里的 源 指的是有没有自带震荡电路 xff0c 有源的蜂鸣器自带有震荡电路 xff0c 通电的瞬间就会发出声音 xff1b 而无源的蜂鸣器 xff0c 需要提供一个2 5
  • JVM虚拟机

    JVM 1 JVM 概述 x1f6b4 x1f6b4 x1f6b2 x1f6b4 虚拟机 xff08 Virtual Machine xff09 是一台虚拟的计算机 VMware属于系统虚拟机 xff0c 是对物理计算机的仿真 Java虚拟
  • 树莓派桌面WIFI图标消失,树莓派黑屏can‘t currently show the desktop

    方法一 xff1a 重装镜像 方法二 xff1a 找个树莓派显示器终端输入这行代码 sudo apt install wpasupplicant wpagui libengine pkcs11 openssl 转载B站视频 xff1a 完美
  • cuda10.1+cudnn10.1+tensorflow2.2.0+pytorch1.7.1下载安装及配置

    一 cuda及cudnn下载 1 查看自己电脑是否支持GPU 方法 xff1a 鼠标移动到此电脑 xff0c 点击鼠标右键 xff0c 依次选择属性 设备管理器 显示适配器有以下图标 xff08 NVIDIA xff09 即可安装GPU x
  • C语言:strtok()函数简单用法

    strtok函数 切割字符串 第一个参数指定一个字符串 xff0c 它包含了0个或者多个由第二个参数 xff08 字符串 xff09 中的一个或多个分隔符分割的标记 strtok函数找到第一个参数中的下一个标记 xff0c 并将其用 39
  • ESP32之FreeRTOS--任务的创建和运行

    文章目录 前言一 创建任务和删除函数1 xTaskCreate 2 xTaskCreateStatic 3 xTaskCreateRestricted 4 vTaskDelete 二 任务函数和任务控制块TCB1 任务函数模板2 TCB 三
  • 如何将本地项目上传到gitee

    如何将本地项目上传到gitee 第一步 xff1a 首先你要有一个gitee仓库 新建仓库 填写仓库信息 xff1a 如图 第二步 xff1a 将创建好的仓库 xff0c pull xff08 拉取 xff09 到本地 通过git 命令 把
  • go语言操作es

    目录 go语言操作es解决golang使用elastic连接elasticsearch时自动转换连接地址初始化数据创建结构体方式字符串方式 xff1a 查找修改删除查找 集群搭建配置文件修改 go语言操作es go get github c
  • Context介绍

    目录 Context设计原理默认上下文取消信号传值方法小结 Context 上下文 context Context Go 语言中用来设置截止日期 同步信号 xff0c 传递请求相关值的结构体 上下文与 Goroutine 有比较密切的关系
  • 将视频转成ROS的bag包

    执行转化命令 python2 mp4 2 bag py lane video3 mp4 out camera bag 循环播放图片 xff0c 并重命名成自己需要的话题名 rosbag play l out camera bag camer
  • beego介绍(一)

    目录 beego 的 MVC 架构介绍参数配置默认配置解析不同级别的配置多个配置文件支持环境变量配置系统默认参数基础配置App 配置Web配置监听配置Session配置Log配置 路由设置基础路由基本 GET 路由基本 POST 路由注册一
  • TCP如何保证可靠性?

    TCP如何保证可靠性 xff1f TCP协议保证数据传输可靠性的方式主要有 xff1a 校验和 序列号 确认应答 超时重传 连接管理 流量控制 拥塞控制 1 校验和 计算方式 xff1a 在数据传输的过程中 xff0c 将发送的数据段都当做
  • 仿照java的jdk动态代理实现go语言动态代理

    仿照java的jdk动态代理实现go语言动态代理 通过学习java的jdk动态代理和Cglib动态代理 xff0c 仿照jdk动态代理用go实现了一个简单的动态代理 结构型模式 代理模式 代理模式中分为静态代理和动态代理 静态代理需要在编译
  • golang设计模式——装饰器模式

    装饰器模式 装饰器模式 xff1a 动态地给一个对象添加一些额外的职责 xff0c 就增加功能来说 xff0c 装饰模式比生成子类更为灵活 UML类图 xff1a 分析 首先我们需要理解 xff0c 为什么组合优于继承 xff1f 继承有诸