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

Go 的每一个框架都在用的设计模式——装饰器模式

Go 的每一个框架都在用的设计模式——装饰器模式不修改一行源码,如何让函数"无限增强"?揭秘 Go 框架背后的核心设计模式一、从一个真实问题开始假设你正在开发一个 HTTP 服务,需要给核心业务函数添加以下功能:// 核心业务函数 func HandleRequest(w http.ResponseWriter, r *http.Request) { // 处理请求... w.Write([]byte("Hello, World!")) }需求来了:添加日志记录(记录每个请求的 URL、耗时)添加认证检查(验证 Token)添加限流保护(防止恶意请求)添加 panic 恢复(防止服务崩溃)添加指标监控(Prometheus metrics)** naive 的做法:**func HandleRequest(w http.ResponseWriter, r *http.Request) { // 1. 日志 start := time.Now() log.Printf("Request: %s", r.URL) defer log.Printf("Duration: %v", time.Since(start)) // 2. 认证 token := r.Header.Get("Authorization") if !validateToken(token) { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } // 3. 限流 if !limiter.Allow() { http.Error(w, "Too Many Requests", http.StatusTooManyRequests) return } // 4. Panic 恢复 defer func() { if err := recover(); err != nil { log.Printf("Panic recovered: %v", err) http.Error(w, "Internal Server Error", http.StatusInternalServerError) } }() // 5. 指标监控 metrics.RequestCounter.Inc() // 6. 核心业务逻辑(终于看到了!) w.Write([]byte("Hello, World!")) }问题:❌ 核心业务逻辑被"横切关注点"淹没❌ 代码难以测试(每个功能耦合在一起)❌ 想给其他 handler 添加相同功能?复制粘贴!❌ 修改任何一个功能都可能影响其他功能有没有一种方式,能不修改原函数,动态添加这些功能?答案是:装饰器模式(Decorator Pattern)二、装饰器模式核心思想2.1 什么是装饰器模式?装饰器模式:动态地给对象添加新的功能,而不改变其原有结构。用 Go 的话说:用一个函数包装另一个函数,在调用前后添加额外逻辑。2.2 核心结构┌─────────────────────────────────────────┐ │ 原始函数 (Handler) │ │ func(w, r) { 业务逻辑 } │ └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ 装饰器 1 (LoggingDecorator) │ │ func(w, r) { 日志; 调用原始函数 } │ └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ 装饰器 2 (AuthDecorator) │ │ func(w, r) { 认证; 调用装饰器 1 } │ └─────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────┐ │ 装饰器 3 (RateLimitDecorator) │ │ func(w, r) { 限流; 调用装饰器 2 } │ └─────────────────────────────────────────┘关键:装饰器本身和被装饰的对象遵循相同的接口!三、Go 中的装饰器模式实现3.1 HTTP Handler 装饰器// 1. 日志装饰器 func LoggingMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { start := time.Now() // 调用前:记录请求 log.Printf( "Started %s %s from %s", r.Method, r.URL.Path, r.RemoteAddr, ) // 调用原始 handler next.ServeHTTP(w, r) // 调用后:记录耗时 log.Printf( "Completed %s %s in %v", r.Method, r.URL.Path, time.Since(start), ) }) } // 2. 认证装饰器 func AuthMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { token := r.Header.Get("Authorization") if token == "" { http.Error(w, "Missing authorization token", http.StatusUnauthorized) return } if !validateToken(token) { http.Error(w, "Invalid authorization token", http.StatusUnauthorized) return } // 认证通过,调用下一个 handler next.ServeHTTP(w, r) }) } // 3. 限流装饰器 func RateLimitMiddleware(limiter *rate.Limiter) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if !limiter.Allow() { http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests) return } next.ServeHTTP(w, r) }) } // 4. Panic 恢复装饰器 func RecoveryMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { defer func() { if err := recover(); err != nil { log.Printf("Panic recovered: %v", err) debug.PrintStack() http.Error(w, "Internal Server Error", http.StatusInternalServerError) } }() next.ServeHTTP(w, r) }) } // 5. 指标监控装饰器 func MetricsMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // 包装 ResponseWriter 以捕获状态码 rw := responseWriter{ResponseWriter: w, statusCode: http.StatusOK} start := time.Now() next.ServeHTTP(rw, r) duration := time.Since(start) // 记录指标 metrics.RequestCounter.WithLabelValues(r.URL.Path, rw.statusCode).Inc() metrics.RequestDuration.WithLabelValues(r.URL.Path).Observe(duration.Seconds(

相关文章:

Go 的每一个框架都在用的设计模式——装饰器模式

Go 的每一个框架都在用的设计模式——装饰器模式 不修改一行源码,如何让函数"无限增强"?揭秘 Go 框架背后的核心设计模式 一、从一个真实问题开始 假设你正在开发一个 HTTP 服务,需要给核心业务函数添加以下功能: // 核心业务函数 func HandleRequest(w http.R…...

Go 开发者都在用的 Option 模式,彻底告别丑陋构造函数

Go 开发者都在用的 Option 模式,彻底告别丑陋构造函数 函数式编程思想在 Go 工程实践中的优雅落地 一、痛点:你被"爆炸式参数列表"折磨过吗? 先看一段熟悉的代码: // 传统构造函数方式 func NewServer(addr string, port int, timeout time.Duration, maxConn …...

UARDECS库:AVR平台UECS协议轻量级嵌入式实现

1. UARDECS库概述:面向日本设施园艺的UECS协议嵌入式实现UARDECS(Universal Agricultural Remote Data Exchange Communication Standard)库是一个专为Arduino平台设计的轻量级通信协议栈,其核心目标是将日本设施园艺领域广泛采用…...

新手必看:Keil5+nRF52832烧录蓝牙程序全流程(附Jlink调试技巧)

从零开始掌握Keil5与nRF52832蓝牙开发全流程 1. 开发环境搭建与硬件准备 工欲善其事,必先利其器。在开始nRF52832蓝牙开发之前,我们需要做好充分的准备工作。首先需要确认硬件设备齐全:一块nRF52832开发板(如Nordic官方的nRF52 DK…...

Youtu-VL-4B-Instruct参数详解:n-gpu-layers设置对显存占用与速度的影响实测

Youtu-VL-4B-Instruct参数详解:n-gpu-layers设置对显存占用与速度的影响实测 1. 引言:为什么我们需要关注n-gpu-layers? 如果你正在使用腾讯优图实验室开源的Youtu-VL-4B-Instruct模型,特别是通过GGUF格式在本地部署&#xff0c…...

FaceFusion快速部署教程:一键运行,Nvidia/AMD显卡全平台支持

FaceFusion快速部署教程:一键运行,Nvidia/AMD显卡全平台支持 1. FaceFusion简介 FaceFusion是一款革命性的AI换脸工具,它通过深度学习技术实现了高质量的人脸替换功能。与传统的换脸软件不同,FaceFusion具有以下核心优势&#x…...

STM32F407内部FLASH数据管理实战:从存储结构到安全读写

1. STM32F407内部FLASH的存储结构解析 第一次拿到STM32F407芯片时,我对着数据手册研究了半天它的FLASH结构。这就像买房前要先看户型图一样,了解存储结构是进行数据管理的基础。STM32F407的FLASH主要分为两大区域:主存储块和信息块。主存储块…...

别再只盯着Python了!用GeNIe SMILE和BayesiaLab快速上手贝叶斯网络建模(附实战对比)

别再只盯着Python了!用GeNIe & SMILE和BayesiaLab快速上手贝叶斯网络建模(附实战对比) 贝叶斯网络作为概率图模型的重要分支,正在医疗诊断、金融风控、工业预测等领域展现出独特价值。但许多从业者在工具选择上陷入两难&…...

DeepSeek-OCR-2惊艳效果:91.09%准确率真实测试展示

DeepSeek-OCR-2惊艳效果:91.09%准确率真实测试展示 1. 突破性的OCR识别技术 DeepSeek-OCR-2代表了当前OCR技术的最前沿水平。这款由DeepSeek团队开发的第二代光学字符识别模型,在2026年1月发布后立即引起了广泛关注。它最引人注目的特点是在OmniDocBen…...

Pixel Dimension Fissioner智能助手:客服话术动态优化与风格迁移实战

Pixel Dimension Fissioner智能助手:客服话术动态优化与风格迁移实战 1. 引言:当客服话术遇上像素魔法 在客服行业,每天面对大量重复性问题时,如何保持话术的专业性和新鲜感是个永恒挑战。传统方法要么依赖固定话术模板显得刻板…...

一键部署:nanobot轻量级AI助手快速体验,QQ聊天机器人搭建不求人

一键部署:nanobot轻量级AI助手快速体验,QQ聊天机器人搭建不求人 1. 开箱即用:你的第一个AI助手,5分钟就能跑起来 想拥有一个属于自己的AI助手,但又觉得技术门槛太高、部署太麻烦?今天,我来带你…...

Asian Beauty Z-Image Turbo 效果展示:基于Transformer架构生成的高质量人像作品集

Asian Beauty Z-Image Turbo 效果展示:基于Transformer架构生成的高质量人像作品集 最近在AI图像生成圈子里,Asian Beauty Z-Image Turbo这个名字被讨论得挺多。它主打的是生成具有亚洲美学特征的高质量人像,据说在细节和风格上都有不错的表…...

嵌入式系统中排序算法选型与优化实践

1. 常用排序算法实现与工程实践分析在嵌入式系统开发中,排序算法虽不似通信协议或实时调度机制那般显性关键,却在数据采集、传感器校准、日志归档、GUI列表渲染等场景中承担着不可替代的基础作用。尤其在资源受限的MCU平台(如Cortex-M0/M3/M4…...

保姆级教程:为你的RK3588设备配置BQ25703充电与CW2017电量计(附完整DTS代码与参数详解)

RK3588设备充电与电量计配置实战指南 在嵌入式开发领域,电源管理系统的稳定性直接影响设备用户体验。对于采用RK3588芯片组并运行Android 12系统的设备来说,BQ25703充电控制器与CW2017电量计的协同工作尤为关键。本文将深入解析这两个核心组件的配置要点…...

用Comsol探索金属合金凝固的数值模拟世界

comsol数值模拟。 金属合金凝固数值模拟,连铸过程数值模拟,相场流场温度场,坯壳厚度计算。在材料科学和金属加工领域,数值模拟已经成为一种强大的工具,帮助我们深入理解复杂的物理过程。今天,咱们就来唠唠用…...

动态协同平衡理论在AI领域的创新应用:构建稳健、自适应与可信赖的智能系统

动态协同平衡理论在AI领域的创新应用:构建稳健、自适应与可信赖的智能系统一、核心思想:以动态协同平衡重塑AI系统本质 动态协同平衡理论的核心思想——“系统的稳定性本质不在于消除变化,而在于通过结构冗余与动态调控的协同,主动…...

告别重复造轮子:基于Vxe-Table二次封装的5个高级技巧与避坑指南

Vxe-Table深度封装实战:5个提升开发效率的进阶方案 在复杂业务场景下,表格组件的灵活性和扩展性往往成为前端开发的关键瓶颈。许多团队在采用Vxe-Table后会发现,虽然基础功能完善,但面对动态高度计算、自定义合计行等实际需求时&a…...

OmenSuperHub终极指南:如何彻底掌控你的惠普游戏本性能与散热

OmenSuperHub终极指南:如何彻底掌控你的惠普游戏本性能与散热 【免费下载链接】OmenSuperHub 项目地址: https://gitcode.com/gh_mirrors/om/OmenSuperHub 你是否曾经对游戏本那"自作主张"的性能调度感到无奈?是否在游戏关键时刻因为温…...

Cheat Engine实战:5步搞定游戏内存指针扫描(附重启验证技巧)

Cheat Engine实战:5步精准定位游戏内存指针与重启验证技巧 引言 在游戏修改领域,内存指针扫描是破解动态地址变动的核心技术。不同于简单的数值搜索,指针扫描能穿透游戏的多层内存结构,直达数据存储的核心位置。想象一下这样的场景…...

保姆级教程:用SD卡给迪文DMG80480C043_01WTC串口屏烧录程序的完整流程

迪文DMG80480C043_01WTC串口屏SD卡烧录全流程实战指南 在工业控制和智能设备开发领域,迪文串口屏因其稳定性和易用性广受开发者青睐。本文将详细介绍如何通过SD卡为DMG80480C043_01WTC型号串口屏烧录程序的完整流程,从工具准备到最终验证,每个…...

Wan2.1 VAE模型微调实战:使用自定义数据集训练专属风格模型

Wan2.1 VAE模型微调实战:使用自定义数据集训练专属风格模型 你是不是也遇到过这样的问题?看到别人用AI生成的图片风格独特、效果惊艳,但自己用同样的模型,却怎么也调不出那种感觉。或者,你的品牌有一套固定的视觉规范…...

Unsloth Studio:一键微调LLM

在本地运行和微调AI模型一直很强大,但很少简单。大多数开发者仍然需要处理脚本、配置和多个工具才能让基本的管道工作。这种摩擦正是Unsloth Studio试图消除的。 Unsloth Studio将使用开源模型的整个生命周期带入一个本地界面。从加载模型到创建数据集、训练、评估…...

【书生·浦语】internlm2-chat-1.8b部署教程:Mac M1/M2芯片原生适配方案

【书生浦语】internlm2-chat-1.8b部署教程:Mac M1/M2芯片原生适配方案 1. 模型简介与特点 InternLM2-1.8B是书生浦语第二代系列中的轻量级版本,拥有18亿参数。这个版本专门为资源受限环境设计,特别是在Mac M1/M2芯片上能够高效运行。 当前…...

宝塔面板MySQL安装报错?手把手教你修改panelPlugin.py文件解除限制

突破宝塔面板MySQL安装限制的深度解决方案 当你在低配置服务器上尝试通过宝塔面板安装MySQL时,是否遇到过这样的提示:"至少需要2个CPU核心才能安装"或"内存不足XXXMB"?这种限制常常让开发者陷入两难——要么升级服务器配…...

Redis 分布式锁的五大深坑与实战解法

在单体架构时代,遇到并发问题,我们直接上 synchronized 或者 ReentrantLock 就能轻松搞定。但一到微服务、分布式时代,这些本地锁就集体罢工了。这时候,我们通常会请出 Redis 来救场,实现分布式锁。很多人拍脑袋一想&a…...

OLLMA部署本地大模型轻量化标杆:LFM2.5-1.2B-Thinking边缘AI落地全景图

OLLMA部署本地大模型轻量化标杆:LFM2.5-1.2B-Thinking边缘AI落地全景图 1. 引言:当大模型“瘦身”成功,走进你的口袋 你是否曾想过,让一个功能强大的AI助手常驻在你的个人电脑、笔记本,甚至是手机里,随时…...

达梦数据库日志避坑指南:遇到‘Fail to find file‘等FATAL错误时必做的3步抢救流程

达梦数据库FATAL级故障应急手册:从日志分析到数据抢救的完整路径 当数据库控制台突然弹出鲜红的"FATAL"字样时,任何DBA的肾上腺素都会飙升。不同于普通的ERROR告警,FATAL错误往往意味着数据库服务已经或即将停止工作。去年某电商大…...

ESP8266 AT指令透传开发实战:MCU+WiFi模组协同设计指南

1. 项目概述本项目聚焦于ESP8266 WiFi通信模组的底层通信验证与典型物联网功能实现,核心目标是建立一套可复现、可扩展的AT指令调试与网络数据交互验证流程。区别于SDK二次开发模式,本方案采用MCUESP8266的经典分立架构,将ESP8266严格定位为透…...

详解Java包装类

一、什么是包装类?核心作用是什么?包装类,本质上是Java为8种基本数据类型分别提供的“封装类”,它将基本数据类型的值封装成对象,赋予其对象的特性(可以调用方法、实现接口、作为泛型参数等)。每…...

从TSP到CVRP:用经典聚类+量子路由破解物流容量限制难题

量子计算与经典算法融合:破解物流路径优化中的容量约束挑战 当物流企业面对每天数千个配送点的路径规划时,传统算法往往在计算时间和解决方案质量之间陷入两难。这种被称为"容量约束车辆路径问题"(CVRP)的挑战,已经成为制约现代物流…...