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 是常用的,在初始化配置函数经常用

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/705832.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

centos升级g++.v6.1.0版本

1.下载源码包 wget http://ftp.gnu.org/gnu/gcc/gcc-6.1.0/gcc-6.1.0.tar.gz tar -zxvf gcc-6.1.0.tar.gz cd gcc-6.1.0 2.打开 download_prerequisites 脚本 vim contrib/download_prerequisites 可以看到该文件就是执行一些下载指令&#xff0c;需要下载几个包&#xff0c…

顺序表的列题(力扣)和旋转数组

文章目录 一.删除有序数组中的重复项&#xff08;取自力扣&#xff09; 二.合并两个有序数组&#xff08;取自力扣&#xff09; 三.旋转数组&#xff08;多解法&#xff09; 前言 见面我们说到了顺序表今天来分享几个有关于顺序表的题目 一.删除有序数组中的重复项&#xff…

数据结构2月21日

双向链表: func函数&#xff1a; #include <stdio.h> #include <stdlib.h> …

【Flink精讲】Flink性能调优:内存调优

内存调优 内存模型 JVM 特定内存 JVM 本身使用的内存&#xff0c;包含 JVM 的 metaspace 和 over-head 1&#xff09; JVM metaspace&#xff1a; JVM 元空间 taskmanager.memory.jvm-metaspace.size&#xff0c;默认 256mb 2&#xff09; JVM over-head 执行开销&#xff1…

【web】云导航项目部署及环境搭建(复杂)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、项目介绍1.1项目环境架构LNMP1.2项目代码说明 二、项目环境搭建2.1 Nginx安装2.2 php安装2.3 nginx配置和php配置2.3.1 修改nginx文件2.3.2 修改vim /etc/p…

LeetCode--134

134. 加油站 在一条环路上有 n 个加油站&#xff0c;其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车&#xff0c;从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发&#xff0c;开始时油箱为空。 给定两个整数数组 …

Camera metadata设计与应用

前言 Android的Camera Metadata是一种数据结构&#xff0c;用于表示图像特征的参数&#xff0c;例如常见的曝光、AE、AWB、flash等参数。在新的Camera API2 / HAL3架构中&#xff0c;这种结构被用来在app-hal之间IPC传输&#xff0c;取代了原先的SetParameter()/GetParameter(…

【蓝桥杯单片机入门记录】动态数码管

目录 一、数码管动态显示概述 二、动态数码管原理图 &#xff08;1&#xff09;原理图 &#xff08;2&#xff09;动态数码管如何与芯片相连 &#xff08;3&#xff09;“此器件” ——>锁存器74HC573 三、动态数码管显示例程 &#xff08;1&#xff09;例程1&#xf…

独立站谷歌SEO外包与自建SEO团队:哪个更适合您的业务?

随着数字营销的崛起&#xff0c;搜索引擎优化&#xff08;SEO&#xff09;已成为企业提升在线可见度、吸引潜在客户的关键手段。面对独立站谷歌SEO外包服务和自建SEO团队两种选择&#xff0c;企业往往会感到困惑。本文将深入探讨这两种方式的优势与局限&#xff0c;帮助您做出明…

CentOS 7.9上编译wireshark 3.6

工作环境是Centos 7.9&#xff0c;原本是通过flathub安装的wireshark&#xff0c;但是在gnome的application installer上升级到wireshark 4.2.3之后就运行不起来了&#xff0c;flatpak run org.wireshark.Wireshark启动提示缺少qt6&#xff0c;查了一下wireshark新版是依赖qt6的…

<HarmonyOS第一课>运行Hello World

本课程是基于HarmonyOS 3.1/4.0版本的新技术和特性所推出的系列化课程&#xff0c;每个课程单元里面都包含视频、Codelab、文章和习题&#xff0c;帮助您快速掌握HarmonyOS的应用开发&#xff1b; 通过本章节的学习&#xff0c;您可以安装DevEco Studio开发工具&#xff0c;运行…

docker-集成测试搭建

dockerd守护进程 Dockerd&#xff08;Docker Daemon&#xff09;是 Docker 引擎的守护进程&#xff0c;是运行在后台的一个持续运行的进程&#xff0c;负责管理 Docker 容器、镜像、网络和存储等核心功能。它是 Docker 容器的守护进程&#xff0c;负责接收 Docker API 请求并管…

java面试:Seata 分布式事务

文章目录 引言I Seata 分布式事务1.1 Seata的整体架构1.2 使用 Seata 进行分布式事务管理的步骤1.3 配置Seata Server1.4 Seata分布式模式1.5 高可用II XA模式III TA模式3.1 TA的写隔离3.2 AT模式的优缺点3.3 实现AT模式IV TCC模式 (Try-Confirm-Cancel)补偿事务4.1 空回滚和拒…

Java应用通过jmx_exporter对外暴露jvm指标

示范代码 public class App {public static void main( String[] args ) throws InterruptedException {while(true){Thread.sleep(10000);System.out.println( "Hello World!" );}} } maven打包,生成test-prometheus-1.0-SNAPSHOT.jar 编写config.yaml lowercas…

RISC-V SoC + AI | 在全志 D1「哪吒」开发板上,跑个 ncnn 神经网络推理框架的 demo

引言 D1 是全志科技首款基于 RISC-V 指令集的 SoC&#xff0c;主核是来自阿里平头哥的 64 位的 玄铁 C906。「哪吒」开发板 是全志在线基于全志科技 D1 芯片定制的 AIoT 开发板&#xff0c;是目前还比较罕见的使用 RISC-V SoC 且可运行 GNU/Linux 操作系统的可量产开发板。 n…

Wireshark TS | Linux 系统对时问题

问题描述 节前业务运维同事提交了一个 case &#xff0c;说是部署在新业务区域的 Linux 服务器和老业务区域的 Linux 服务器无法对时&#xff0c;脚本里使用的是 clockdiff 命令&#xff0c;无法正常返回结果&#xff0c;而在老业务区域两台服务器之间执行命令就正常&#xff…

桥接模式(Bridge Pattern)

桥接模式&#xff08;Bridge Pattern&#xff09; 定义 将抽象部分与它的实现部分分离&#xff0c;使它们都可以独立的变化。 属于结构性模式 理解 合成/聚合复用原则的具体实现&#xff0c;将一个整体的多个实现部分独立出来&#xff0c;每个部分都可以独立的变化&#x…

OSI参考模型和TCP/IP网络参考模型

1、OSI参考模型 1.1 产生背景 为了解决网络之间的兼容性问题,实现网络设备间的相互通讯,国际标准化组织ISO于1984年提出了OSIRM(Open System Interconnection Reference Model,开放系统互连参考模型)。OSI参考模型很快成为计算机网络通信的基础模型。由于种种原因,并没有…

android stadio通过wifi热点 adb连接android

问题&#xff1a;有时后忘记带android usb线了&#xff0c;但需要调试 解决办法&#xff1a; 1、准备开发工具adb&#xff0c;需要到sdk下的platform-tools的目录下&#xff0c;或者把这个目录加到系统path里&#xff0c;我的是D:\android\sdk\platform-tools这个目录&#x…

Mysql整理-Mysql事务

MySQL中的事务是一组顺序执行的数据库操作,要么完全执行,要么完全不执行。事务是数据库管理的一个重要概念,尤其是在确保数据完整性和一致性方面。MySQL中的事务遵循ACID属性,这是事务性数据库系统的四个关键特性: 原子性(Atomicity):事务内的所有操作都是作为一个单一…