Golang Wrapper for Receiver Function

Kelvin Benzali
CodeX
Published in
3 min readApr 2, 2022

The wrapper discusses here is a design pattern in Golang of wrapping the functions of a struct with another function. The wrapper function can run a set of executions before or after the original function without changing it. You may find this similar to the terms of adapter, proxy, or decorator design pattern.

An object or class in Object-Oriented Programming can be represented as the struct in Golang. A receiver function is a method of a struct that has a receiver(its struct) in the function argument list. Tour Go

Basic Wrapper

Unlike receiver function, a normal function can be wrapped with a function that receives the original function as an argument and return it as an output.

type handlerFunc func(param int) errorfunc Wrapper(aFunc handlerFunc) handlerFunc {
return func(param int) error {
// Do wrapper stuff
defer func() { // Do post-wrapper stuff }()
return aFunc(param)
}
}

Receiver function has a different story. It has its own struct that act as a specialized argument. Hence, the function is always has access to the struct values. Basically, receiver function is a function defined inside the struct type and can only be called from the struct. In order to make a wrapper of such function, it can be achieved with basic normal wrapper like before.

type app struct {}func (app *app) Run() error {
// Execute application
}
func appRun(app *app) error {
// Do wrapper stuff
defer func() { // Do post-wrapper stuff }
return app.Run()
}
func main() {
myApp := &app{}
// Run without wrapper
err := myApp.Run()
// Run with wrapper
err = appRun(myApp)
}

Although this wrapper method is simple, it has a lot of drawbacks.

  • What if the struct has a lot of functions and wants the wrapper effect as well?
  • How to implement the wrapper to all the downstream dependencies that are using the receiver function?
  • What happens if the struct has new a function that is needed the wrapper effect as well?

Receiver Wrapper

The kind of wrapper for the receiver function must be scalable and reliable. Scalable means the wrapper supports any kind of expansion of the receiver function without much effort to maintain it. Reliable means the wrapper produces consistent results and performance across the dependencies at all times.

One of the solutions is to make a new wrapper struct to mimic the original struct. In order to fully imitate the original struct, the wrapper struct would have all the functions identical to the original receiver functions. Finally, the wrapper receiver function would contain pre or post instructions before or after calling the original one.

Furthermore, this method can be applied to integrate the wrapper into any existing code without changing its behavior and structure while still scalable and reliable in the future.

Applications

Utility Services

Utility services that can be applied to the wrapper can be anything, to be honest. For example, the common use is caching, validation, authentication, authorization, or monitoring tools. Other services or instructions that are not related to the function purposes can also be executed in a wrapper instead.

Logging

Printing out data or information to the log before or after the function might be useful in the wrapper. Especially, when the data need to be processed or restructured first before printing out to the log for analyzing or monitoring purposes.

Versioning

Suppose we have a feature that is deprecated and needs to be updated with a new version. The problem is that the code has a massive downstream dependency and needs to be rolled out partially without affecting many parties. Wrapping the functions with validation is one of the solutions. This way, any requests that are compatible with the new version will be redirected to the newer version, while the other request still uses the older version.

--

--

Kelvin Benzali
CodeX
Writer for

Software Engineer at Tokopedia. Technology and history enthusiast.