news 2026/6/6 17:39:53

代码开发,常用的几种设计模式【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/6/6 4:27:13

毕业设计项目 基于设深度学习的人脸性别年龄识别系统

文章目录0 前言1 课题描述课题意义2 实现效果3 算法实现原理3.1 数据集3.2 深度学习识别算法3.3 特征提取主干网络3.4 总体实现流程4 具体实现4.1 预训练数据格式4.2 部分实现代码0 前言 今天学长向大家介绍一个基于深度学习机器视觉的项目&#xff0c;人脸性别年龄识别系统&am…

作者头像 李华
网站建设 2026/6/6 11:12:27

21、Docker在DevOps管道与大规模应用中的实践

Docker在DevOps管道与大规模应用中的实践 1. Docker与应用测试 1.1 Docker测试优势 Docker的一个关键优势在于能在与生产环境完全相同的运行环境中测试应用及其所有依赖。虽然它无法保证对数据库等外部依赖进行了充分测试,也不提供神奇的测试框架,但能确保库和其他代码依赖…

作者头像 李华
网站建设 2026/6/6 10:31:32

华为OD机试双机位C卷 - 停车场收入统计 (C++ Python JAVA JS GO)

停车场收入统计 2025华为OD机试双机位C卷 - 华为OD上机考试双机位C卷 100分题型 华为OD机试双机位C卷真题目录点击查看: 华为OD机试双机位C卷真题题库目录|机考题库 + 算法考点详解 题目描述 给定一个停车场某一天的车辆出入记录,请计算该停车场的当日收入,收费规则如下:…

作者头像 李华