When we define an object, usually it creates a convenient method to initialize an instance external. As in the following example:
type Client struct {
timeout int64
dialFunc func() error
healthCheck func() bool
}
func NewClient(timeout int64, dialFunc func() error, healthCheck func() bool) *Client {
return &Client{
timeout: timeout,
dialFunc: dialFunc,
healthCheck: healthCheck,
}
}
External calls NewClient
to get an instance, NewClient
the method needs to Client structure Each parameter assignment, if the parameter Client object There are dozens, then call the NewClient
need to pass dozens of parameters, but if you just want to change one parameter, other parameters want to use the default, also you need to pass all parameters, very inconvenient.
Optimization can set up a method for each parameter Set method , NewClient
each parameter are set to default values, complete external call NewClient
after call SetXX method .
If you are interested in NewClient
the on-time configuration, and can be achieved through functional option:
We first define configuration options option
, option
it is a func
, to the Senate is *Client
an example, we can modify the value of an instance in it.
type NewClientOptions func(c *Client)
Then we modify the NewClient
approach to the Senate as a variable length option
, we first create an instance, the values are the default values, and then call option
, modify the value of the instance.
func NewClient2(opts ...NewClientOptions) *Client {
client := &Client{
timeout: defaultTimeout,
dialFunc: defaultDialFunc,
healthCheck: defaultHealthCheckFunc,
}
for _, opt := range opts {
opt(client) //opt是个方法,入参是*Client,内部会修改client的值
}
return client
}
Finally, we define several option methods:
func WithTimeout(timeout int64) NewClientOptions {
return func(c *Client) {
c.timeout = timeout
}
}
func WithHealthCheck(healthCheck func() bool) NewClientOptions {
return func(c *Client) {
c.healthCheck = healthCheck
}
}
func WithDial(dialFunc func() error) NewClientOptions {
return func(c *Client) {
c.dialFunc = dialFunc
}
}
Such external calls can be simpler NewClient
method, and a definition of our own myHealthCheckFunc
, and through WithHealthCheck
package it, and then passed as a parameter on the line.
client := NewClient2(WithHealthCheck(myHealthCheckFunc), WithTimeout(10))
Client instances created timeout
and healthCheck
our own definition, which dialFunc
is the default.
In fact, by passing this func
way it is more common configuration parameters, such as grpc
when establishing a connection Dial
method, this approach is actually used.
grpcClient, err = grpc.Dial(
serviceTarget(target),
grpc.WithBalancer(b),
grpc.WithCompressor(grpc.NewGZIPCompressor()),
grpc.WithDecompressor(grpc.NewGZIPDecompressor()),
grpc.WithDefaultCallOptions(grpc.FailFast(false)),
grpc.WithInsecure(),
grpc.WithBlock(),
grpc.WithTimeout(time.Second*5),
grpc.WithUnaryInterceptor(grpc_middleware.ChainUnaryClient(
otgrpc.OpenTracingClientInterceptor(opentracing.GlobalTracer(), otgrpc.LogPayloads()),
unaryClientInterceptor,
)),
)