2026/4/18 5:31:04
网站建设
项目流程
网站需求分析问题,网站建设与网页制作基础入门教程,如何让商家建设网站,wordpress后台中文设置一、创建型模式#xff08;处理对象创建#xff09;
1. 单例模式#xff08;Singleton#xff09;
用途#xff1a;保证一个结构体在程序运行期间只有一个实例(确保全局只有一个实例被new出来)#xff0c;并提供全局访问点#xff08;如配置管理器、日志器#xff09;
…一、创建型模式处理对象创建1. 单例模式Singleton用途保证一个结构体在程序运行期间只有一个实例(确保全局只有一个实例被new出来)并提供全局访问点如配置管理器、日志器Go 实现特点利用 sync.Once 保证线程安全是 Go 中最优雅的单例实现方式读取配置文件案例var(instance*Config once sync.Once)funcGetConfig()*Config{once.Do(func(){instanceConfig{Port:8080}})returninstance}2. 工厂模式Factory用途封装对象创建逻辑根据不同条件返回不同的结构体实例Go 实现特点通常用函数返回接口。获取不同类型数据库链接句柄typeDBinterface{Query(string)string}typeMySQLstruct{}func(m MySQL)Query(qstring)string{returnMySQL: q}typePgSQLstruct{}func(m PgSQL)Query(qstring)string{returnPgSQL: q}// 工厂函数funcNewDB(dbTypestring)DB{switchdbType{casemysql:returnMySQL{}casepgsql: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{returnMilkDecorator{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 TargetNewHandle{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.payMethodmethod}// 执行支付func(cart*ShoppingCart)Checkout(amountfloat64)string{ifcart.payMethodnil{returnNo 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{returnServer{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方法时只想设置port和worker参数中间这个参数我还得想办法传递个零值或默认值进去不然代码会报错现在换一种写法通过增加代码量来提升维护性typeServerstruct{PortintTimeout time.Duration}typeOptionfunc(*Server)funcWithPort(portint)Option{returnfunc(s*Server){s.Portport}}funcWithTimeout(t time.Duration)Option{returnfunc(s*Server){s.Timeoutt}}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读写等……