What's the most idiomatic way of initializing a Go type with many required parameters?
For example:
type Appointment struct {
Title string
Details string
Dresscode string
StartingTime int64
EndingTime int64
RSVPdate int64
Place *Place
Guests []*Guest
}
type Place struct {
Name string
Address string
}
type Guest struct {
Name string
Status string
}
I want the Appointment type to be always valid; that is, I don't want to initialize it with a struct literal and then have to validate it.
Don't want:
a := &Appointment{
Title: "foo",
Details: "bar",
StartingTime: 12451412,
...
}
err := a.Validate()
whats the best way to initialize this type of object (with lots of fields) without having to supply all the arguments in the constructor arguments?
You may be able to use the "functional options" pattern to achieve this. It allows you to define functions for each input, removing the need for you to pass lots of options to your constructor.
func New(options ...func(*Appointment)) (*Appointment, error) {
ap := &Appointment{
Title: "Set your defaults",
Details: "if you don't want zero values",
StartingTime: 123,
}
for _, option := range options {
option(ap)
}
// Do any final validation that you want here.
// E.g. check that something is not still 0 value
if ap.EndTime == 0 {
return nil, errors.New("invalid end time")
}
return ap, nil
}
// Then define your option functions
func AtEndTime(endTime int64) func(*Appointment) {
return func(ap *Appointment) {
ap.EndTime = endTime
}
}
The resulting call looks something like:
ap, err := appointment.New(
AtEndTime(123),
WithGuests([]Guest{...}),
)
If you want to validate each option in the function itself, it's not too much work to change that signature to possibly return an error too.
One way you could avoid having to pass 10+ arguments to your constructors is to have an XxxParams type for each of your Xxx types and have your NewXxx take that params type as its argument. Then the NewXxx constructor would construct an Xxx value from those params, validate it, and return it, or an error, depending on the result of the validation.
This might feel redundant if you're constructing the XxxParams values manually as opposed to unmarshaling them from json, xml, etc.; but still, this way you are enforcing, however loosely, only valid Xxx's to be constructed, keeping the possibly invalid state in the input (XxxParams).
Here's an example from Stripe's repo: Account, AccountParams, and constructor
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With