This paper describes the application of functional languages Go mode options and the design patterns in the actual programming.
Why do we need functional option mode?
I recently go-micro / options.go source code, I found a piece of code about the service registration are as follows:
type Options struct {
Broker broker.Broker
Cmd cmd.Cmd
Client client.Client
Server server.Server
Registry registry.Registry
Transport transport.Transport
// Before and After funcs
BeforeStart []func() error
BeforeStop []func() error
AfterStart []func() error
AfterStop []func() error
// Other options for implementations of the interface
// can be stored in a context
Context context.Context
}
func newOptions(opts ...Option) Options {
opt := Options{
Broker: broker.DefaultBroker,
Cmd: cmd.DefaultCmd,
Client: client.DefaultClient,
Server: server.DefaultServer,
Registry: registry.DefaultRegistry,
Transport: transport.DefaultTransport,
Context: context.Background(),
}
for _, o := range opts {
o(&opt)
}
return opt
}
At that time it is not very clear newOptions
this constructor why you wrote, but also to see people behind the recurrence of similar code to ask why you wrote in the micro-channel group, and later, when discussed in the group know that it is a design patterns - functional mode option .
May we see now is not quite understand what I'm talking about the problem in the end is that I keep it simple and refined look.
We now have a structure defined as follows:
type Option struct {
A string
B string
C int
}
Now we need to prepare for a constructor, we could write the following in this way:
func newOption(a, b string, c int) *Option {
return &Option{
A: a,
B: b,
C: c,
}
}
The above code is well understood, but also we have been written. Any questions?
We now consider the following two questions:
- We may need to specify default values for Option Fields
- Option field member may change happen
Options mode
We first define a OptionFunc
function type
type OptionFunc func(*Option)
It is then prepared using a closure set value for each field With function:
func WithA(a string) OptionFunc {
return func(o *Option) {
o.A = a
}
}
func WithB(b string) OptionFunc {
return func(o *Option) {
o.B = b
}
}
func WithC(c int) OptionFunc {
return func(o *Option) {
o.C = c
}
}
Then, we define a default Option
as follows:
var (
defaultOption = &Option{
A: "A",
B: "B",
C: 100,
}
)
Finally, we write the new constructor as follows:
func newOption2(opts ...OptionFunc) (opt *Option) {
opt = defaultOption
for _, o := range opts {
o(opt)
}
return
}
have a test:
func main() {
x := newOption("nazha", "小王子", 10)
fmt.Println(x)
x = newOption2()
fmt.Println(x)
x = newOption2(
WithA("沙河娜扎"),
WithC(250),
)
fmt.Println(x)
}
Output:
&{nazha 小王子 10}
&{A B 100}
&{沙河娜扎 B 250}
Such a style option using the constructor function design pattern is realized. Such defaults have been, and then later want to add a new field to the Option will not affect the previous code.
Recommended reading: