工厂函数返回接口还是具体类型

2024/05/06 Go 踩过的坑 共 1319 字,约 4 分钟

问题背景

在 Go 语言的编程实践中,我们经常遇到需要通过工厂函数创建对象的情况。工厂函数是一种设计模式,用于处理对象的创建,并封装其创建过程。在 Go 中,这通常涉及到定义一个返回具体类型或接口类型的函数。

最近,我在写 kitexcall 的项目时,遇到了一个关于工厂函数返回类型的问题。项目中有一个用于创建 Thrift 客户端的工厂函数,其原始定义如下:

package client
import (
    "genericclient"
    "generic"
    "config"
    "callopt"
)
type Client interface {
    _// 客户端接口方法..._
}
type ThriftGeneric struct {
    GenericClientBase
    Provider generic.DescriptorProvider
}
type GenericClientBase struct {
    Client      genericclient.Client
    Generic     generic.Generic
    Conf        *config.Config
    ClientOpts  []Option
    CallOptions []callopt.Option
    Resp        interface{}
}
func NewThriftGeneric() Client {
    return &ThriftGeneric{}
}

在上述代码中,NewThriftGeneric 函数旨在返回一个新的 ThriftGeneric 实例,该实例实现了 Client 接口。然而,在调用此函数并尝试访问 Resp 字段时,编译器报出了错误:

// 断言检查,确保响应不为空且包含预期的数据
if cli.Resp == nil { // cli.Resp这里报错
    t.Fatalf("Response is nil")
}
    
    
compilerMissingFieldOrMethod: type "github.com/kitex-contrib/kitexcall/pkg/client".Client has no field or method Resp

这表明 Client 接口中并没有定义 Resp 字段,尽管 ThriftGeneric 结构体中确实存在这样一个字段。

解决方式

为了解决这个问题,我重新审视了 NewThriftGeneric 函数的返回类型。我意识到,由于 Resp 字段是 ThriftGeneric 结构体的一部分,而不是 Client 接口的一部分,因此直接返回 Client 类型是不正确的。为了能够访问 Resp 字段,我将函数的返回类型从 Client 接口更改为指向 ThriftGeneric 的指针:

func NewThriftGeneric() *ThriftGeneric {
    return &ThriftGeneric{}
}

通过这个修改,编译器不再报错,因为现在函数返回的是一个指向具体类型 ThriftGeneric 的指针,该类型确实包含了 Resp 字段。

最佳实践

在设计工厂函数时,以下是一些最佳实践的考虑:

  1. 接口优先:如果调用者只需要使用接口定义的方法,那么返回接口类型可以提供更好的封装和抽象。
  2. 明确意图:如果你的设计意图是允许调用者访问具体类型的所有方法,包括那些未在接口中定义的方法,那么返回具体类型的指针是合适的。

文档信息

Search

    Table of Contents