• golang使用高阶函数优化业务功能


    业务描述

            两个接口(新增Tag和更新Tag),在业务层均需要添加两个校验,校验Tag信息是否重复和Tag的数据中的编码是否重复。

    基本实现

    方式

            对应的增加两个校验的函数/方法,在接口实现中依次调用两个函数/方法进行校验。

    优缺点

            实现简单;但重复代码多,后期再增加其他校验,扩展性较差。

    高阶函数方式一

    方式

            因为业务方法参数相同,业务层新增一个方法,包含一个函数类型参数(该函数即最终业务函数,新增/更新Tag),业务层新增方法:

    1. func (t *TagUseCase) ValidateTag(ctx context.Context, req *Tag, f func(context.Context, *Tag) error) error {
    2. //校验一
    3. exists, err := t.repo.ValidateAppIdFieldExists(ctx, req.AppId, req.Field, req.IdString)
    4. if err != nil {
    5. return err
    6. }
    7. if exists {
    8. return errors.New(fmt.Sprintf("已存在:%v", req.Field))
    9. }
    10. //校验二
    11. flag, code := t.ValidateCodeUnique(req.Children)
    12. if flag {
    13. return errors.New(fmt.Sprintf("编码重复:%v", code))
    14. }
    15. //执行业务目标函数
    16. return f(ctx, req)
    17. }

    服务层调用:

    1. func (s *TagService) CreateTag(ctx context.Context, req *pb.CreateTagRequest) (*pb.OperationTagReply, error) {
    2. //s.tuc.CreateTag为目标函数
    3. err := s.tuc.ValidateTag(ctx, tag, s.tuc.CreateTag)
    4. if err != nil {
    5. return nil, errors.New(0, err.Error(), "failed!")
    6. }
    7. return &pb.OperationTagReply{Msg: "success"}, nil
    8. }

    优缺点

            通过将目标函数参数化,将校验抽取到了一个方法中,后期如果增加其他校验,只需修改ValidateTag方法即可,有点类似于Java中的静态代理。重复代码很少,扩展性较好。但如果不同的业务需要的校验不完全相同,则存在问题。

    高阶函数方式二

    方式

            通过中间件实现(借鉴Kratos框架middleware),校验函数和目标函数的入参和返回值均相同(即使不同,可通过golang的闭包将函数包装成需要的函数签名),将其都作为一次处理,将所有的处理链接起来再执行。

    定义中间件Handler类型:

    1. 定义中间件
    2. type Handler func(ctx context.Context, req interface{}) error #定义handler类型,即定义中间件和最终执行方法/函数的的声明
    3. type Middleware func(Handler) Handler # 定义Middle类型,即入参和返回值为相同类型的Handler,用于后面将其链接起来
    4. # 将各中间件链接起来,next即最终要执行的函数,通过反向遍历的方式,将中间件按照添加的顺序依次链接,最先添加的在最外层,最先执行,结构类似:func(func(func(next)))
    5. func Chain(m ...Middleware) Middleware {
    6. return func(next Handler) Handler {
    7. for i := len(m) - 1; i >= 0; i-- {
    8. next = m[i](next)
    9. }
    10. return next
    11. }
    12. }

    在业务层创建中间件:

    1. func (t *TagUseCase) ValidatorTagExists() tagmiddleware.Middleware {
    2. return func(handler tagmiddleware.Handler) tagmiddleware.Handler {
    3. return func(ctx context.Context, req interface{}) (err error) {
    4. if v, ok := req.(*Tag); ok {
    5. exists, err := t.repo.ValidateAppIdFieldExists(ctx, v.AppId, v.Field, v.IdString)
    6. if err != nil {
    7. return err
    8. }
    9. if exists {
    10. return errors.New(fmt.Sprintf("该标签field已存在:%v", v.Field))
    11. }
    12. }
    13. return handler(ctx, req)
    14. }
    15. }
    16. }

    调用方式一:

    1. #添加中间件:在业务层的结构体增加一个middleware字段,创建结构体时,将需要的中间件添加到该属性中
    2. t.middleware = []tagmiddleware.Middleware{t.ValidatorTagExists(), t.ValidatorTagCodeRepeat()}
    3. #提供给需要中间件的方法/函数调用: Chain首先获取到执行链,最终传入h进行调用
    4. func (t *TagUseCase) Middleware(h tagmiddleware.Handler) tagmiddleware.Handler {
    5. return tagmiddleware.Chain(t.middleware...)(h)
    6. }
    1. 最终调用:首先构建中间件,UpdateTag是最终要执行的方法。middleware(ctx,tag) 执行中间件和目标方法
    2. middleware := s.tuc.Middleware(func(ctx context.Context, disposeReq interface{}) error {
    3. return s.tuc.UpdateTag(ctx, disposeReq.(*biz.Tag))
    4. })
    5. err = middleware(ctx, tag)

    调用方式二:方式一提前指定了要执行那些中间件,不够灵活

    1. #最终调用:在调用时指定要执行那些中间件。
    2. middlewarex := tagmiddleware.Chain([]tagmiddleware.Middleware{s.tuc.ValidatorTagExists(), s.tuc.ValidatorTagCodeRepeat()}...)(func(ctx context.Context, disposeReq interface{}) error {
    3. return s.tuc.UpdateTag(ctx, disposeReq.(*biz.Tag))
    4. })
    5. err = middlewarex(ctx, tag)

    优缺点

            将校验和目标函数都作为handler处理,在调用时可以自定义设置要进行那些校验,灵活性高。但后期增加新的校验时,需要在多个调用的位置将新的校验handler添加到执行链中。

  • 相关阅读:
    关于DP动规
    html文件中引入.ts文件并运行
    OpenVPN Connect使用连接公网VPN服务器实现内网穿透
    2022人工智能学习--完整入门攻略
    微信支付业务代码流程
    简单对比一下 C 与 Go 两种语言
    在win上配置pytorch用到的一些命令(PyCharm & Anaconda)
    asp.net乡村旅游管理系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio
    vue.js毕业设计,基于vue.js前后端分离在线小说电子书阅读小程序系统 开题报告
    LeetCode知识点总结 - 376
  • 原文地址:https://blog.csdn.net/q79030/article/details/132969206