news 2026/8/6 21:38:24

代码开发,常用的几种设计模式【golang】

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
代码开发,常用的几种设计模式【golang】

一、创建型模式(处理对象创建)

1. 单例模式(Singleton)

用途:保证一个结构体在程序运行期间只有一个实例(确保全局只有一个实例被new出来),并提供全局访问点(如配置管理器、日志器)

Go 实现特点:利用 sync.Once 保证线程安全,是 Go 中最优雅的单例实现方式

读取配置文件案例

var(instance*Config once sync.Once)funcGetConfig()*Config{once.Do(func(){instance=&Config{Port:8080}})returninstance}
2. 工厂模式(Factory)

用途:封装对象创建逻辑,根据不同条件返回不同的结构体实例

Go 实现特点:通常用函数返回接口。

获取不同类型数据库链接句柄

typeDBinterface{Query(string)string}typeMySQLstruct{}func(m MySQL)Query(qstring)string{return"MySQL: "+q}typePgSQLstruct{}func(m PgSQL)Query(qstring)string{return"PgSQL: "+q}// 工厂函数funcNewDB(dbTypestring)DB{switchdbType{case"mysql":returnMySQL{}case"pgsql":returnPgSQL{}default:panic("unsupported db")}}

二、结构型模式(处理对象组合)

1. 装饰器模式(Decorator)

装饰器模式(Decorator Pattern)是一种结构型设计模式,核心目标是:在不修改原有对象代码的前提下,动态地给对象添加额外的功能。

用途:动态添加功能(如日志、限流、认证)

Go 实现特点:体现有多种,如函数嵌套、组合 + 接口方式继承重写方法等

标准库http服务实现案例

funcmain(){http.HandleFunc("/api",myHandler)http.ListenAndServe(":8080",nil)}// 业务逻辑funcmyHandler(w http.ResponseWriter,r*http.Request){w.WriteHeader(http.StatusOK)w.Write([]byte("Hello from decorated handler!"))// ……}

这是标准库的内置方法,http.HandleFunc定义为func HandleFunc(pattern string, handler func(ResponseWriter, *Request))

现在我要给我的业务逻辑方法加个日志,又不想改动其代码,现在给它包裹上一层

funcmain(){handler:=withLogging(myHandler)http.HandleFunc("/api",handler)http.ListenAndServe(":8080",nil)}funcwithLogging(next http.HandlerFunc)http.HandlerFunc{returnfunc(w http.ResponseWriter,r*http.Request){log.Printf("Received request: %s %s",r.Method,r.URL.Path)next(w,r)// 这里是业务逻辑方法}}// 业务逻辑funcmyHandler(w http.ResponseWriter,r*http.Request){w.WriteHeader(http.StatusOK)w.Write([]byte("Hello from decorated handler!"))}

通过重新定义一个闭包函数,加点日志代码,对其原有业务逻辑进行装饰

另一种继承式写法,计算咖啡价格

// 咖啡typeCoffeeinterface{Cost()int// 价格}// 接口实现:咖啡价格typeBlackCoffeestruct{}func(b*BlackCoffee)Cost()int{return10}// 装饰器1:咖啡加牛奶的价格typeMilkDecoratorstruct{coffee Coffee// 嵌套咖啡接口}func(m*MilkDecorator)Cost()int{returnm.coffee.Cost()+2// 基础价格+牛奶价格}// 传入实现Coffee接口的实例,返回另一个实现Coffee接口的实例funcNewMilkDecorator(coffee Coffee)Coffee{return&MilkDecorator{coffee:coffee}}funcmain(){// 基础黑咖啡coffee:=&BlackCoffee{}fmt.Printf("价格:%d\n",coffee.Cost())// 价格:10// 加牛奶(装饰)coffeeWithMilk:=NewMilkDecorator(coffee)fmt.Printf("价格:%d\n",coffeeWithMilk.Cost())// 黑咖啡 + 牛奶, 价格:12}
2. 适配器模式(Adapter)

用途:将一个结构体的接口转换成客户端期望的另一个接口,解决接口不兼容问题

Go 实现特点:适配器结构体嵌套原结构体,实现目标接口

继承老的业务逻辑,新增新的接口实现

// 旧的代码定义typeOldhandlestruct{}func(a*Oldhandle)SpecificRequest()string{return"旧接口的具体实现"}// 现在感觉 SpecificRequest 名字不好听,改成 Request// 希望的接口定义typeTargetinterface{Request()string}// 适配器:将 Oldhandle 适配为Target接口typeNewHandlestruct{Oldhandle*Oldhandle}// 实现 Target 接口,内部调用 Oldhandle 的方法func(a*NewHandle)Request()string{return"适配后:"+a.Oldhandle.SpecificRequest()}funcmain(){// 客户端只依赖 Target 接口,无需关心 Oldhandle 的具体实现vartarget Target=&NewHandle{Oldhandle:&Oldhandle{}}target.Request()// 输出: 适配后:旧接口的具体实现}

装饰器与适配器非常类似,几乎没什么不同,通过类型嵌套方式继承功能并改造

三、行为型模式(处理对象交互)

1. 策略模式(Strategy)

用途:定义一系列算法,将每个算法封装起来并可互相替换,使算法独立于使用它的客户端(如支付方式、排序算法切换)。

Go 实现特点:通过接口定义算法行为,不同策略实现该接口,客户端动态切换策略。

微信支付、支付宝支付,业务逻辑选择

// 1. 支付接口定义(策略定义)typePayInterinterface{Pay(amountfloat64)string// 输入金额,返回支付结果}// 2. 策略可选列表// 2.1 支付宝typeAlipaystruct{}func(a Alipay)Pay(amountfloat64)string{returnfmt.Sprintf("Paid %.2f via Alipay",amount)}// 2.2 微信支付typeWechatPaystruct{}func(w WechatPay)Pay(amountfloat64)string{returnfmt.Sprintf("Paid %.2f via WeChat",amount)}// 3. 策略执行// 购物车支付场景typeShoppingCartstruct{payMethod PayInter}// 设置购物车支付场景用哪种方式支付func(cart*ShoppingCart)SetPayMethod(method PayInter){cart.payMethod=method}// 执行支付func(cart*ShoppingCart)Checkout(amountfloat64)string{ifcart.payMethod==nil{return"No payment method set"}returncart.payMethod.Pay(amount)}funcmain(){cart:=&ShoppingCart{}cart.SetPayMethod(Alipay{})fmt.Println(cart.Checkout(99.9))// Paid 99.90 via Alipaycart.SetPayMethod(WechatPay{})fmt.Println(cart.Checkout(199.0))// Paid 199.00 via WeChat}

这种方式本质上还是结构体嵌套,继承

2. 选项模式

用途:优雅地设置可选参数(替代构造函数重载)

广泛用于代码量大的类库封装(如 http.Server, grpc.DialOption)

初始化一个结构体示例

typeServerstruct{PortintTimeout time.Duration}funcNewServer(portint,timeout time.Duration)*Server{return&Server{Port:port,Timeout:timeout,}}funcTestManual(t*testing.T){server:=NewServer(3000,5*time.Second)t.Log(server.Port)}

通过NewServer方法初始化Server结构,Server的参数有十几个,NewServer里面的参数定义要写很多,不美观,比方说

NewServer(port int, timeout time.Duration, worker int)我现在调用NewServer方法时,只想设置portworker参数,中间这个参数我还得想办法传递个零值或默认值进去,不然代码会报错

现在换一种写法,通过增加代码量来提升维护性

typeServerstruct{PortintTimeout time.Duration}typeOptionfunc(*Server)funcWithPort(portint)Option{returnfunc(s*Server){s.Port=port}}funcWithTimeout(t time.Duration)Option{returnfunc(s*Server){s.Timeout=t}}funcNewServer(opts...Option)*Server{s:=&Server{Port:8080}for_,opt:=rangeopts{opt(s)}returns}// 业务逻辑funcTestManual(t*testing.T){server:=NewServer(WithPort(3000),WithTimeout(5*time.Second))t.Log(server.Port)}

现在调用NewServer方法时,想自定义哪个参数,就传递指定配置方法即可,也不用操心顺序,反正是个切片类型

四、管道/过滤器模式(Pipeline / Filter)

用途:通过 channel 串联处理步骤(典型 Go 并发模式)

基于 goroutine + channel

我习惯称之为一个小的MapReduce实现

// 这里生产数据,预处理funcgen(nums...int)<-chanint{out:=make(chanint)gofunc(){for_,n:=rangenums{out<-n}close(out)}()returnout}// 这里加工数据funcsq(in<-chanint)<-chanint{out:=make(chanint)gofunc(){forn:=rangein{out<-n*n}close(out)}()returnout}// 这里通过range读取chan展示数据funcmain(){forn:=rangesq(gen(1,2,3)){fmt.Println(n)}}

剩下的还有观察者模式等等一大堆写法,总的来说无非都是闭包、结构体继承、chan读写等……

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/6 21:31:29

计算机毕业设计springboot金象传动公司工资管理信息系统开发 基于Spring Boot框架的金象传动公司薪酬管理系统开发 金象传动公司工资管理信息化平台设计与实现

计算机毕业设计springboot金象传动公司工资管理信息系统开发k5gpm9&#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。随着信息技术的飞速发展&#xff0c;企业对工资管理的信息化需…

作者头像 李华
网站建设 2026/8/5 17:46:23

毕设 基于大数据挖掘分析的大众点评评论文本挖掘

文章目录1.前言2.爬虫1.1 整体思路1.2 网页爬取和解析1.3 数据存储反爬虫对抗2 探索性分析与文本数据预处理2.1 探索性分析2.2 数据预处理2.3词云展示3 文本的情感分析3.1 先上结果3.2 文本特征提取&#xff08;TF-IDF&#xff09;3.3 机器学习建模3.4 最后输出的准确率1.前言 …

作者头像 李华
网站建设 2026/8/5 15:26:05

27、Docker 容器安全深度解析

Docker 容器安全深度解析 1. 命名空间与容器隔离 命名空间是让容器看起来像容器的关键因素。通过 nsenter 命令,我们可以精确指定要进入的命名空间,甚至可以只进入临时容器命名空间的一部分来进行探索。结合 cgroups,命名空间能在同一内核上为进程提供较为强大的隔离。 …

作者头像 李华
网站建设 2026/8/6 2:08:11

AdaBoost算法之葡萄酒案例

数据加载与预处理 首先用 pandas 读取葡萄酒数据集&#xff0c;原本数据集的标签有1、2、3三类&#xff0c;因为决策树更适配二分类任务&#xff0c;所以过滤掉标签为1的样本&#xff0c;只保留2、3类&#xff1b;再用 LabelEncoder 将类别标签[2,3]转换成模型能识别的数值[0,1…

作者头像 李华
网站建设 2026/8/6 6:04:45

使用Qwen3-14B进行法律文书辅助撰写的合规性探讨

使用Qwen3-14B进行法律文书辅助撰写的合规性探讨 在律师事务所的某个深夜&#xff0c;一位年轻律师正为第二天开庭的案件赶制答辩状。他反复核对《民法典》条文、比对类似判例、调整措辞逻辑——这本是再普通不过的一幕&#xff0c;但如今&#xff0c;这样的场景正悄然发生变化…

作者头像 李华
网站建设 2026/8/6 1:26:31

2025 网安副业入门:5 个低门槛方向,零基础也能接的第一单

2025 网安副业入门&#xff1a;5 个低门槛方向&#xff0c;零基础也能接的第一单 “学了半个月 Kali&#xff0c;想赚点外快却不知道从哪下手”“怕技术不够接不了单&#xff0c;又怕定价太高没人要”—— 这是 90% 网安新手想做副业时的共同困境。2025 年网安副业市场需求旺盛…

作者头像 李华