Hello,
I’d contemplate Useful Choices Sample. On the optimistic, it permits condensing a number of constructors right into a single one, with risk of extending it later, with out breaking consumer code, and gives shoppers with smart defaults (globalLogger in your instance). Draw back is that the code appears a bit troublesome, till you get the cling of it.
First it is advisable add two choices structs for every of your structs, which comprise all defaultable choices
sort FooOptions struct {
logger Logger
}
...
sort BarOptions struct {
logger Logger
Foo *Foo
}
Then, maintain solely NewFoo and NewBar constructors as follows
// Params earlier than choices - 'a' on this case,
//these with out which service can't work,
// and also you, as implementor, can't know defaults for
// (for instance, an API Key)
// choices - for params that may be defaulted (like Logger)
func NewFoo(a int, choices ...func(*FooOptions)) *Foo {
// outline defaults
ops := &FooOptions{}
ops.logger = globalLogger
// overwrite configuration with offered choices
for _, possibility := vary choices {
possibility(ops)
}
// configure Foo primarily based on ultimate set of choices
return &Foo{a: a, logger: ops.logger}
}
...
func NewBar(a int, b bool, choices ...func(*BarOptions)) *Bar {
// outline defaults
ops := &BarOptions{}
ops.logger = globalLogger
ops.Foo = NewFoo(a) // Foo With Default Logger
// overwrite configuration with offered choices
for _, possibility := vary choices {
possibility(ops)
}
// configure Foo primarily based on ultimate set of choices
return &Bar{b: b, logger: ops.logger, foo: ops.Foo}
}
Now, you’ll be able to outline choices funcs as follows, for comfort
func FooWithLogger(logger Logger) func(*FooOptions) {
return func(ops *FooOptions) {
ops.logger = logger
}
}
...
func BarWithLogger(logger Logger) func(*BarOptions) {
return func(ops *BarOptions) {
ops.logger = logger
}
}
func BarWithFoo(foo *Foo) func(*BarOptions) {
return func(ops *BarOptions) {
ops.Foo = foo
}
}
And now your consumer can use it like proven beneath
func major() {
// now you'll be able to both have bar with default logger, and default Foo
_ = NewBar(2, true)
// or present every little thing
_ = NewBar(2, false, BarWithLogger(Logger{}), BarWithFoo(NewFoo(3, FooWithLogger(Logger{}))))
}
Hope this helps