当前位置: 首页 > news >正文

golang 装饰器模式详解

前言

我一直以来对golang的装饰器模式情有独衷,不是因为它酷,而是它带给我了太多的好处。首先我不想说太多的概念,熟记这些概念对我的编程来说一点用处没有。我只知道它给我带来了好处,下面谈谈我的理解。

这种模式可以很轻松地把一些函数装配到另外一些函数上,让你的代码更加简单,也可以让一些“小功能型”的代码复用性更高,让代码中的函数可以像乐高玩具那样自由地拼装。

重要的是你不用修改代码以前的功能,对以前的功能没有影响,而是动态的,很方便的扩展函数的功能。下面我将举几个例子说明下

golang 的装饰器通常用interface{} 以及 anonymous functions 实现的,下面我们看看实际的例子

一、interface{} 实现装饰器

type Printer interface {Print() string
}
​
type SimplePrinter struct {}
​
func (sp *SimplePrinter) Print() string {return "Hello, world!"
}
​
func BoldDecorator(p Printer) Printer {return PrinterFunc(func() string {return "<b>" + p.Print() + "</b>"})
}
​
type PrinterFunc func() string
​
func (pf PrinterFunc) Print() string {return pf()
}
​
func main() {simplePrinter := &SimplePrinter{}boldPrinter := BoldDecorator(simplePrinter)fmt.Println(simplePrinter.Print()) // Output: Hello, world!fmt.Println(boldPrinter.Print()) // Output: <b>Hello, world!</b>
}
  1. 在上面的代码中我们定义了一个Printer接口,一个 SimplePrinter 结构体实现了Print方法

  2. 我们定义了 BoldDecorator 函数接受一个Printer接口返回一个Printer接口.该函数将原来的 Print() 方法封装到一个新的方法中,该方法返回的是用 <b> 标记括起来的相同值

  3. 这只是一个简单的例子,却展示了装饰器的强大的功能。通过添加新的装饰器,我们可以在运行时改变对象的行为,而无需更改其原始代码。当我们需要为一个已经存在的对象添加新功能,而又想保持其原始代码不变时,装饰器模式就显得尤为有用。这样,我们就可以避免为每一个想要添加的新功能创建新的子类。

二、Http 相关的装饰器的例子

func WithServerHeader(h http.HandlerFunc) http.HandlerFunc {return func(writer http.ResponseWriter, request *http.Request) {writer.Header().Set("server", "0.01")h(writer, request)}
}
​
func withServerSetCook(h http.HandlerFunc) http.HandlerFunc {return func(writer http.ResponseWriter, request *http.Request) {cookie := http.Cookie{Name: "username", Value: "tt"}http.SetCookie(writer, &cookie)h(writer, request)}
}
​
func WithBasicAuth(h http.HandlerFunc) http.HandlerFunc {return func(writer http.ResponseWriter, request *http.Request) {cookie, err := request.Cookie("username")if err != nil || cookie.Value != "ee" {writer.WriteHeader(http.StatusForbidden)return}h(writer, request)}
}
​
func WithDebugLog(h http.HandlerFunc) http.HandlerFunc {return func(writer http.ResponseWriter, request *http.Request) {request.ParseForm()log.Println(request.Form)log.Println("path", request.URL.Path)log.Println("Host", request.URL.Host)log.Println(request.Form["url_long"])for k, v := range request.Form {log.Println("key:", k)log.Println("value:", v)}h(writer, request)}
}
​
func hello(w http.ResponseWriter, r *http.Request) {log.Printf("Recieved Request %s from %s\n", r.URL.Path, r.RemoteAddr)fmt.Fprintf(w, "Hello, World! "+r.URL.Path)
}
​
func main() {http.HandleFunc("/hello/v1", WithServerHeader(hello))http.HandleFunc("/hello/v2", withServerSetCook(hello))http.HandleFunc("/hello/v3", WithBasicAuth(hello))http.HandleFunc("/hello/v4", WithDebugLog(hello))err := http.ListenAndServe(":8080", nil)if err != nil {log.Fatal("ListenAndServe: ", err)}
}
  1. 例子中 WithServerHeader,withServerSetCook,WithBasicAuth, WithDebugLog 就是一个装饰器,它传入一个 http.HandlerFunc 就是一个改写版本。而我们的业务hello不用修改任何功能,可以呈现出一些新的功能,很多人把这种模式称为middleware ,我更喜欢称为装饰器

三、多个装饰器Pipeline,也是options 模式

type googleSlide struct {sreSlide *list.Listinterval int64mutex    sync.Mutexk        int64
}type slideVal struct {time   int64req    int64accept int64
}type SlideValOptions func(val *slideVal)func NewSlideval(options ...SlideValOptions) *slideVal {t := &slideVal{time: time.Now().UnixNano(),}for _, option := range options {option(t)}return t
}func WithReqOption(req int64) SlideValOptions {return func(val *slideVal) {val.req = req}
}func WithAcceptReqOption(accept int64) SlideValOptions {return func(val *slideVal) {val.accept = accept}
}func NewGoogleSlide(interval time.Duration, k int64) *googleSlide {return &googleSlide{sreSlide: list.New(),interval: interval.Nanoseconds(),k:        k,}
}func (g *googleSlide) Sre() grpc.UnaryClientInterceptor {return func(ctx context.Context, method string, req, reply any, cc *grpc.ClientConn, invoker grpc.UnaryInvoker, opts ...grpc.CallOption) error {g.mutex.Lock()now := time.Now().UnixNano()front := g.sreSlide.Front()for front != nil && front.Value.(*slideVal).time+g.interval < now {g.sreSlide.Remove(front)front = g.sreSlide.Front()}var r, accept int64front = g.sreSlide.Front()for front != nil {t := front.Value.(*slideVal)r += t.reqaccept += t.acceptfront = front.Next()}tail := (float64(r) - float64(g.k*accept)) / (float64(r) + 1)if tail > 0 {g.mutex.Unlock()return errors.New("request is fail")}g.sreSlide.PushBack(NewSlideval(WithReqOption(1)))err := invoker(ctx, method, req, req, cc, opts...)if err == nil {g.sreSlide.PushBack(NewSlideval(WithAcceptReqOption(1)))}g.mutex.Unlock()return err}
}

这个代码是我在SRE 熔断器写的代码,现在重新拿出来,是因为具有代表性 。NewSlideval 可以支持多个装饰器,遍历装饰器,就可以得倒我们想要的功能,只要我们去实现这个装饰器。这样的代码,在golang 是常用的,在初始化配置函数经常用

相关文章:

golang 装饰器模式详解

前言 我一直以来对golang的装饰器模式情有独衷&#xff0c;不是因为它酷&#xff0c;而是它带给我了太多的好处。首先我不想说太多的概念&#xff0c;熟记这些概念对我的编程来说一点用处没有。我只知道它给我带来了好处&#xff0c;下面谈谈我的理解。 这种模式可以很轻松地…...

刷题笔记day27-回溯算法2

216. 组合总和 III 这个思路还是&#xff0c;三部曲&#xff1a; 终止条件处理单层节点回溯节点 题中说的是&#xff0c;1到9的数&#xff0c;不能有重复。 k个数&#xff0c;和为n。 那么只要 len(path) k 的时候&#xff0c;判断 n 为0&#xff0c;就可以入切片了。 fun…...

前端架构: 脚手架命令行交互核心实现之inquirer和readline的应用教程

命令行交互核心实现 核心目标&#xff1a;实现命令行行交互&#xff0c;如List命令行的交互呢比命令行的渲难度要更大&#xff0c;因为它涉及的技术点会会更多它涉及以下技术点 键盘输入的一个监听 (这里通过 readline来实现)计算命令行窗口的尺寸清屏光标的移动输出流的静默 …...

【C++初阶】内存管理

目录 一.C语言中的动态内存管理方式 二.C中的内存管理方式 1.new/delete操作内置类型 2.new和delete操作自定义类型 3.浅识抛异常 &#xff08;内存申请失败&#xff09; 4.new和delete操作自定义类型 三.new和delete的实现原理 1.内置类型 2.自定义类型 一.C语…...

《PyTorch深度学习实践》第十二讲循环神经网络基础

一、RNN简介 1、RNN网络最大的特点就是可以处理序列特征&#xff0c;就是我们的一组动态特征。比如&#xff0c;我们可以通过将前三天每天的特征&#xff08;是否下雨&#xff0c;是否有太阳等&#xff09;输入到网络&#xff0c;从而来预测第四天的天气。 我们可以看RN…...

蓝桥杯算法题汇总

一.线性表&#xff1a;链式 例题&#xff1a;旋转链表 二.栈&#xff1a; 例题&#xff1a;行星碰撞问题 三.队列 三.数组和矩阵 例题&#xff1a; 四.哈希表 五.二叉树 主要方法是递归 主要考察点是遍历&#xff1a;前序&#xff0c;中序&#xff0c;后序遍历&#xff0c;层…...

【MySQL】学习多表查询和笛卡尔积 - 副本

](https://img-blog.csdnimg.cn/21dd41dce63a4f2da07b9d879ad0120b.png#pic_center) ??个人主页: ??热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ??个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-N8PeTKG6uLu4bJuM {font-family:“trebuchet ms”,…...

C++设计模式_创建型模式_工厂方法模式

目录 C设计模式_创建型模式_工厂方法模式 一、简单工厂模式 1.1 简单工厂模式引入 1.2 简单工厂模式 1.3 简单工厂模式利弊分析 1.4 简单工厂模式的UML图 二、工厂方法模式 2.1 工厂模式和简单工厂模式比较 2.2 工厂模式代码实现 2.3 工厂模式UML 三、抽象工厂模式 3.1 战斗场景…...

matlab批量替换txt文本文件的特定行的内容

1.下图所示&#xff0c;我想要替换第14行。 2.运行代码后&#xff0c;第14行已经更改为需要的内容。 clc,clear; %%----------------------需要更改的地方------------------------------------ % 设置要操作的文本文件路径&#xff0c;替换为你自己的文件路径 path D:\paper_…...

Qt Creator配置MSVC编译环境、调试环境

在windows上开发&#xff0c;一般使用Qt Creator自带mingw编译器&#xff0c;编译和调试都很方便&#xff0c;安装Qt时勾选后&#xff0c;自动配置完毕。 但是有时候我们需要使用MSVC的编译器&#xff0c;这个时候我们没法直接使用&#xff0c;需要配置环境才能使用&#xff0…...

Linux系统运维命令:终止监听在 TCP端口80上的所有进程(使用lsof,grep,awk组合命令, 终止监听在 TCP某个端口上的所有进程)

目 录 一、需求 二、解决方法 1、解决思路 2、命令 三、实例演示和命令解释 1、实例演示 &#xff08;1&#xff09;查看目前有哪些在TCP端口80监听的进程 &#xff08;2&#xff09;、使用命令 &#xff08;3&#xff09;、查看效果 2、命令解…...

开源模型应用落地-业务优化篇(七)

一、背景 在本篇学习中,我们要介绍消息中间件,它可以帮助我们将核心和辅助流程分开,让它们互相独立。同时,还要关注在使用消息中间件时需要注意的地方。并且将这种思想应用到其他实际场景中。 二、术语 2.1、消息中间件 消息中间件是一种在分布式系统中用于处理消息传递的…...

序列化-反序列化--json-xml-protoBuf

序列化和反序列化 数据在网络中传输需要按照一定的规范组成。这些规定的规范有json,xml,protobuf。 序列化 也就是说数据需要通过网络传输时&#xff0c;需要把数据转化为需要的传输格式&#xff0c;所以需要把需要传输的数据生成json或者xml或者protobuf语言格式文件&#…...

ubuntu 配置nacos开机启动

在Ubuntu系统上配置Nacos服务开机启动&#xff0c;可以通过创建systemd服务单元文件来实现。以下是步骤&#xff1a; 创建Systemd服务文件&#xff1a; 打开终端&#xff0c;使用文本编辑器&#xff08;如nano或vim&#xff09;新建一个服务文件&#xff1a; sudo nano /etc/sy…...

单节点大数据平台运维脚本

单节点的大数据集群运维脚本 vi /opt/bash/bigdata-operate-script.sh#!/bin/bashsource ~/.bashrc source /etc/profilehostnamebigdata#程序运行必要组件 important_components("kafka" "clickhouse-server" "elasticsearch" "kibana&qu…...

HTML基础知识

目录 1.初识网页 2.html&#xff1a;超文本标记语言 2.1排版标签 标题标签 段落标签 换行标签 水平线标签 2.2文本格式化标签 2.3媒体标签 图片标签 路径 音频标签 视频标签 2.4链接标签 2.5列表标签 2.5.1无序列表 2.5.2有序列表 2.5.3自定义列表 2.6表格…...

牛客禁用题:求阶乘

思路&#xff1a;在新类中使用全局变量进行运算&#xff0c;在主类中定义新类数组&#xff0c;通过构造函数的调用次数返回阶乘 #include <type_traits> class add{public:static int count;static int tmp;add(){countcounttmp;tmp;} }; int add::count0; int add::t…...

spring.factories的常用配置项

概述 spring.factories 实现是依赖 spring-core 包里的 SpringFactoriesLoader 类&#xff0c;这个类实现了检索 META-INF/spring.factories 文件&#xff0c;并获取指定接口的配置的功能。 Spring Factories机制提供了一种解耦容器注入的方式&#xff0c;帮助外部包&am…...

数据库-第二/三章 关系数据库和标准语言SQL【期末复习|考研复习】

前言 总结整理不易&#xff0c;希望大家点赞收藏。 给大家整理了一下计数据库系统概论中的重点概念&#xff0c;以供大家期末复习和考研复习的时候使用。 参考资料是王珊老师和萨师煊老师的数据库系统概论(第五版)。 文章目录 前言第二、三章 关系数据库和标准语言SQL2.1 关系2…...

【办公类-21-05】20240227单个word按“段落数”拆分多个Word(成果汇编 只有段落文字 1拆5)

作品展示 背景需求 前文对一套带有段落文字和表格的word进行13份拆分 【办公类-21-04】20240227单个word按“段落数”拆分多个Word&#xff08;三级育婴师操作参考题目1拆13份&#xff09;-CSDN博客文章浏览阅读293次&#xff0c;点赞8次&#xff0c;收藏3次。【办公类-21-04…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

Oracle查询表空间大小

1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...

关于nvm与node.js

1 安装nvm 安装过程中手动修改 nvm的安装路径&#xff0c; 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解&#xff0c;但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后&#xff0c;通常在该文件中会出现以下配置&…...

java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别

UnsatisfiedLinkError 在对接硬件设备中&#xff0c;我们会遇到使用 java 调用 dll文件 的情况&#xff0c;此时大概率出现UnsatisfiedLinkError链接错误&#xff0c;原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用&#xff0c;结果 dll 未实现 JNI 协…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

html-<abbr> 缩写或首字母缩略词

定义与作用 <abbr> 标签用于表示缩写或首字母缩略词&#xff0c;它可以帮助用户更好地理解缩写的含义&#xff0c;尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时&#xff0c;会显示一个提示框。 示例&#x…...

【笔记】WSL 中 Rust 安装与测试完整记录

#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统&#xff1a;Ubuntu 24.04 LTS (WSL2)架构&#xff1a;x86_64 (GNU/Linux)Rust 版本&#xff1a;rustc 1.87.0 (2025-05-09)Cargo 版本&#xff1a;cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

Selenium常用函数介绍

目录 一&#xff0c;元素定位 1.1 cssSeector 1.2 xpath 二&#xff0c;操作测试对象 三&#xff0c;窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四&#xff0c;弹窗 五&#xff0c;等待 六&#xff0c;导航 七&#xff0c;文件上传 …...