(转)Golang functional options,优雅的初始化对象实例

当我们定义了一个对象时,一般会创建一个方法方便外部初始化一个实例。如下面的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
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,
    }
}

外部调用NewClient来获取一个实例,NewClient方法需要给Client结构体每个参数都赋值,如果Client对象的参数有几十个的话,那么调用NewClient需要传入几十个参数,而且如果用户只是要更改其中一个参数,其他参数都想用默认的,也需要传入所有参数,非常的不方便。

优化的方法一种可以对每个参数设置一个Set方法,NewClient中每个参数都设置默认值,外部调用完NewClient后再调用SetXX方法。

如果想直接在NewClient中就一次性配置好,可以通过functional option来实现:

我们先定义配置选项option,option是一个func,入参是*Client实例,在里面我们可以修改实例的值。

1
type NewClientOptions func(c *Client)

然后我们修改NewClient方法的入参为不定长的option,我们先创建一个实例,值都是默认值,然后调用option,修改实例的值。

1
2
3
4
5
6
7
8
9
10
11
func NewClient2(opts ...NewClientOptions) *Client {
    client := &Client{
        timeout:     defaultTimeout,
        dialFunc:    defaultDialFunc,
        healthCheck: defaultHealthCheckFunc,
    }
    for _, opt := range opts {
        opt(client) //opt是个方法,入参是*Client,内部会修改client的值
    }
    return client
}

最后我们定义几个option方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
    }
}

这样外部就可以更简单的调用NewClient方法了,我们自己定义一个myHealthCheckFunc,并通过WithHealthCheck包一下,然后作为参数传入就行了。

1
client := NewClient2(WithHealthCheck(myHealthCheckFunc), WithTimeout(10))

创建的client实例中timeout和healthCheck是我们自己定义的,而dialFunc是用默认的。

其实这种通过传入func来配置参数的方式是比较常见的,比如grpc在建立连接时的Dial方法,其实就是使用的这种方式。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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,
        )),
    )

转自:https://blog.csdn.net/liyunlong41/article/details/89048382

posted @   人艰不拆_zmc  阅读(251)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
历史上的今天:
2018-03-08 关于SimpleDateFormat时间转换总是显示1970年的问题
点击右上角即可分享
微信分享提示