The website content discusses the advantages of using a configuration function over a configuration object when implementing a New() method for initializing structured objects in Golang.
Abstract
The article advocates for the use of a configuration function to initialize objects in Golang, emphasizing the benefits of this approach over the traditional method of using a configuration object. The author highlights the cleanliness and improved scope hygiene achieved by encapsulating configuration logic within a function, which also enhances testability. This pattern is particularly recommended for complex object initializations that require multiple configuration parameters, such as standardized HTTP server instantiations. By adopting this pattern, developers can avoid messy value checks and reduce code duplication, leading to a more streamlined and maintainable codebase. The author suggests that this method can significantly improve the way developers handle object configuration and encourages readers to try it in their own projects.
Opinions
The author prefers the configuration function pattern for its cleaner code and better scope management.
This pattern is not suitable for simple cases with only one or two configuration parameters; simplicity should prevail in such instances.
The configuration function approach is seen as a way to reduce cyclomatic complexity and verbose error handling inherent in Golang.
The author believes that this pattern can enhance testability by allowing configuration-related variables to be defined within the function's scope.
Using a configuration function is recommended for complex configurations to avoid code duplication and to give consumers direct access to the object's properties.
The author is open to feedback and encourages the community to engage with the content, offering claps or referral bonuses as forms of support and motivation for sharing similar engineering concepts.
How and why to use a function to configure New() in Golang
Because objects are too mainstream?
When implementing a method New() that returns a new instance of some struct, I’ve recently taken a liking to passing a function that configures a new instance rather than passing an object/map of configuration values and letting the New method handle everything.
Here’s why.
Defining our object
Consider our innocently named classobjectInstance:
Using a configuration object
Now consider the following implementation that initialises a new objectInstance using a configuration object:
This results in the consumer code looking like this:
Using a configuration function
Implementing the above so that we use a function instead to initialise this objectInstance instead would look like:
And the consuming code now looks like:
Why and when to use this pattern
I’m pretty sure there’s a pattern name for this that I just haven’t heard of so I’ll refrain from naming it.
First of all, this pattern is obviously not meant to be used in situations where you’re only passing one or two arguments. Defer to keeping things simple in those situations!
This pattern is meant as a cleaner alternative for situations where you absolutely have to pass multiple arguments to configure an instance of an object that was designed to hide complexity from consumers. Think a standardised HTTP server instantiation where you might have to define various timeout values, body size limits, bind interfaces, ports to listen to, et cetera.
With that, here’s what I found really nice about this way of doing things:
Encapsulates the configuration scope
All code related to configuring an objectInstance can be encapsulated in a function’s scope. This improves scope hygiene by allowing you to define variables used only in objectInstance's initialisation in its own configuration function.
If no value from the parent scope needs to be used, you can even completely abstract it out which also improves testability:
Avoid messy value checks
Golang is extremely verbose when it comes to error handling. You already have enough errors to check for, so reduce the number of other checks you have to do.
Using a function to configure the initialiser class allows us to reduce cyclomatic complexity that can quickly increase when there are things to check for:
Which will now look like:
Avoid code duplication in configuration objects
When using an object to configure a function that returns another object, in majority of situations, the returned object instance has many similar properties as its configuration object:
Using a configuration function avoids this completely by:
Giving the consumer access to the object instance’s properties directly while
Enabling validation checks to be run post-consumer-configuration
In conclusion, give it a try and see how it changes your code! Till next time-
If you found the above useful, consider giving some 👏s. It’s free for you and it’ll motivate me to continue sharing on similar concepts in my engineering journey. Alternatively, I’ve just created this referrals page if you’d like to give me a referral bonus for a service I use (in most cases you’ll get something as well).