by Leon Rosenshein

Functional Options

I’ve written about the Builder pattern before. It’s a way to give folks a set of default options for a constructor along with a way to set all of the options, and then validate things before actually creating the object. It’s nice because it lets you set all the parameters, in whatever order you see fit, and only validate them at the end. This can be important if setting parameters one at a time can put things in an invalid state temporarily.

On the other hand, it requires that the initial developer come up with all of the possible combinations of builder parameters. And that might not include the way one of your customers wants to use it. So what can you do in that case?

One variation is the Functional Options pattern. It relies on a variadic “constructor” function. Something along the lines of 

func MakeThing(required1 string, required2 int, options ...func(*Thing)) (*Thing, error)

Then, the user provides one or more functions that take a *Thing and do whatever magic they need to modify it. Those functions might take arguments. They might do validation. They can do anything the language allows. That’s pretty extensible.

Then, inside MakeThing, you add a loop that calls all of those functions, which modify the thing as desired.

func MakeThing(required1 string, required2 int, options ...func(*Thing)) (*Thing, error)
{
    thing := &Thing { 
        Name: required1,
        Value: required2,
    }

    for _, opt := range options {
        opt(thing)
    }

    return thing, nil
}

That gives the user all the control. There are 2 things I don’t like about though. The first is that there’s no final validation at the end. I have yet to see an example/tutorial that has one. It’s trivial to do and I’d certainly add one.

The other is a bigger issue. The functional options pattern requires your users to have full knowledge of the objects internals, while the Builder pattern hides those details. If you’re making a public API you probably want to hide those details, and the validator becomes crucial

Should you use it? Well, it depends, but it’s an option to consider.