代码之家  ›  专栏  ›  技术社区  ›  daniely

在每个函数之前自动运行代码

go
  •  3
  • daniely  · 技术社区  · 8 年前

    我想在许多函数的开头运行一些代码(授权检查),例如:

    func (s *apiService) get(request Request) (Result, error) {
      err := authorizationCheck(request)
      if err != nil {
        return nil, err
      }
      //method logic
    }
    
    func (s *apiService) put(request Request) (Result, error) {
      err := authorizationCheck(request)
      if err != nil {
        return nil, err
      }
      //method logic
    }
    

    是否有一种优雅的方法可以避免在每个函数开始时进行相同的授权检查?

    1 回复  |  直到 8 年前
        1
  •  4
  •   Marco Bonelli    8 年前

    用一个简单的包装

    因为所有的方法(至少我假设是这样)都有相同的签名,所以您可以将所有冗余的代码放在一个包装函数中,这个包装函数将您需要运行的函数作为附加参数。包装器将首先检查错误,然后运行函数。因此,您的方法只需要包含相关的代码,而不必首先检查错误。

    下面是一个例子,我称之为包装器 wrap 只是想说清楚:

    func (s *apiService) get(request Request) (Result, error) {
        //method logic
    }
    
    func (s *apiService) put(request Request) (Result, error) {
        //method logic
    }
    
    func wrap(f func (Request) (Result, error), request Request) (Result, error) {
        err := authorizationCheck(request)
        if err != nil {
            return nil, err
        }
    
        return f(request)
    }
    

    然后,在您的代码后面:

    res, err := wrap(s.get, someRequest)
    

    有一个装饰师

    与上面非常相似,但更干净:您可以实现一个修饰器,而不是创建一个包装器 返回函数 它包装您的方法,并在调用它们之前进行错误检查。只有当所有方法都具有相同的签名时,才能再次执行此操作,但与使用包装器相比,它更强大,而且是一个更清洁的解决方案。

    这是一个例子,装饰师是 decorate (是的,我的创意是这样的):

    func (s *apiService) get(request Request) (Result, error) {
        //method logic
    }
    
    func (s *apiService) put(request Request) (Result, error) {
        //method logic
    }
    
    func decorate(f func(Request) (Result, error)) func(Request) (Result, error) {
        return func(r Request) (Result, error) {
            err := authorizationCheck(r)
            if err != nil {
                return nil, err
            }
    
            return f(r)
        }
    }
    

    然后,在您的代码后面:

    res, err := decorate(s.get)(someRequest)
    

    方法还是简单的函数?

    无论您喜欢一个简单的包装器还是一个装饰器,您也可以使它们成为 apiService 对象(只需添加 (s *apiService) 在它们的名字之前),确实没有什么大的区别,但是如果您希望结构在任何地方都可以使用包装器/装饰器,而不仅仅是在那个特定的文件中,那么这将是首选的选项。

    相对调用将变成:

    res, err := s.wrap(s.get, someRequest)
    

    以及:

    res, err := s.decorate(s.get)(someRequest)