阅读源码时遇到的设计模式

策略模式

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
}

像这样把一系列业务按职责划分成不同的对象,接着把这一系列对象构成一个链,直到“链路结束”为止(结束:异常结束,或链路执行完毕结束)的设计模式,我们称为责任链模式
优点:

  • 直观:一眼可观的业务调用过程
  • 无限扩展:可无限扩展的业务逻辑
  • 高度封装:复杂业务代码依然高度封装
  • 极易被修改:复杂业务代码下修改代码只需要专注对应的业务类(结构体)文件即可,以及极易被调整的业务执行顺序
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值