策略模式
Gin 框架自带的 binding 库,用于将请求体里 JSON、XML、FormData 格式的数据和 URL 上的路径参数、查询字符串、HTTP Headers 绑定到 Go 的 Struct 指针上。通用的方法是ShouldBind,源码如下:
func (c *Context) ShouldBind(obj any) error {
b := binding.Default(c.Request.Method, c.ContentType())
return c.ShouldBindWith(obj, b)
}
其中b是根据请求头的ContentType获取到的绑定器,因此能支持多样格式的请求数据绑定。
像这样 把解析请求数据绑定到对象的任务
定义成一类算法族,把每个解析 绑定算法
封装成不同的绑定器,让客户端可以按照统一的方式使用各种绑定器,这种情况应该使用策略模式进行设计。
策略模式中需要引入一个上下文,作为客户端和具体策略的中间层,用抽象接口去跟具体策略交流,达到客户端能用统一方式使用不同算法的效果,这里的上下文就是 Gin 框架的 Context 来充当的。
参考文章:Go Gin框架请求自动验证和数据绑定,看完这篇就会用了
和工厂模式的区别?
- 核心思想不同: 工厂模式的核心是创建对象,而策略模式的核心是定义一系列可互相替换的算法。 工厂模式关注的是对象的创建过程,策略模式关注的是如何使用这些对象。
- 应用场景不同: 工厂模式通常用于对象的创建和管理。 策略模式用于定义算法族,让它们之间可以互相替换。
- 目的和结构: 工厂模式目的是为了降低对象创建的复杂性,结构上通常包含一个工厂类和多个产品类。 策略模式目的是为了定义策略的多样性,结构上包含策略接口、具体策略类、上下文环境和客户端。
- 使用时机: 工厂模式在对象创建时被调用。 策略模式在对象使用时被调用,以决定具体使用哪个策略。
责任链模式
etcdv3中实现事务的方法
resp, err := client.Txn(ctx).If(cmp).Then(put, getOwner).Else(get, getOwner).Commit()
函数源码:
func (txn *txn) If(cs ...Cmp) Txn {
txn.mu.Lock()
defer txn.mu.Unlock()
if txn.cif {
panic("cannot call If twice!")
}
if txn.cthen {
panic("cannot call If after Then!")
}
if txn.celse {
panic("cannot call If after Else!")
}
txn.cif = true
for i := range cs {
txn.cmps = append(txn.cmps, (*pb.Compare)(&cs[i]))
}
return txn
}
func (txn *txn) Then(ops ...Op) Txn {
txn.mu.Lock()
defer txn.mu.Unlock()
if txn.cthen {
panic("cannot call Then twice!")
}
if txn.celse {
panic("cannot call Then after Else!")
}
txn.cthen = true
for _, op := range ops {
txn.isWrite = txn.isWrite || op.isWrite()
txn.sus = append(txn.sus, op.toRequestOp())
}
return txn
}
func (txn *txn) Else(ops ...Op) Txn {
txn.mu.Lock()
defer txn.mu.Unlock()
if txn.celse {
panic("cannot call Else twice!")
}
txn.celse = true
for _, op := range ops {
txn.isWrite = txn.isWrite || op.isWrite()
txn.fas = append(txn.fas, op.toRequestOp())
}
return txn
}
func (txn *txn) Commit() (*TxnResponse, error) {
txn.mu.Lock()
defer txn.mu.Unlock()
r := &pb.TxnRequest{Compare: txn.cmps, Success: txn.sus, Failure: txn.fas}
var resp *pb.TxnResponse
var err error
resp, err = txn.kv.remote.Txn(txn.ctx, r, txn.callOpts...)
if err != nil {
return nil, toErr(txn.ctx, err)
}
return (*TxnResponse)(resp), nil
}
像这样把一系列业务按职责划分成不同的对象,接着把这一系列对象构成一个链,直到“链路结束”为止(结束:异常结束,或链路执行完毕结束)的设计模式,我们称为责任链模式
。
优点:
- 直观:一眼可观的业务调用过程
- 无限扩展:可无限扩展的业务逻辑
- 高度封装:复杂业务代码依然高度封装
- 极易被修改:复杂业务代码下修改代码只需要专注对应的业务类(结构体)文件即可,以及极易被调整的业务执行顺序