引入

Go 没有 class 也没有构造函数关键字,但创建结构体实例时经常需要做初始化——设置默认值、校验参数、初始化内部字段。Go 社区约定用 New 开头的函数来完成这些工作,形成了统一的构造函数模式。

正文

定义

定义

构造函数模式是 Go 社区约定的对象初始化方式。Go 没有内置构造函数,而是通过 New 开头的普通函数来创建并初始化类型的实例。这种模式也叫工厂模式,调用方不需要了解内部细节,拿到手的就是一个可用的实例。

语法

// 返回指针
func New类型名(参数) *类型名 {
    return &类型名{
        字段: 初始值,
    }
}
 
// 返回错误(初始化可能失败时)
func New类型名(参数) (*类型名, error) {
    if 参数不合法 {
        return nil, errors.New("参数错误")
    }
    return &类型名{字段: 初始值}, nil
}

例子

基本 New 函数

type User struct {
    Name  string
    Age   int
    Email string
}
 
func NewUser(name string, age int) *User {
    return &User{
        Name: name,
        Age:  age,
    }
}
 
func main() {
    u := NewUser("Alice", 25)
    fmt.Println(u.Name, u.Age)
}

NewUser 封装了创建逻辑,调用方不用关心 User 的字段细节。

带默认值

type Config struct {
    Host    string
    Port    int
    Timeout int
}
 
func NewConfig(host string) *Config {
    return &Config{
        Host:    host,
        Port:    8080,
        Timeout: 30,
    }
}

构造函数里设好默认值,调用方只传必要的参数。

带错误返回

type Server struct {
    Addr string
}
 
func NewServer(addr string) (*Server, error) {
    if addr == "" {
        return nil, errors.New("地址不能为空")
    }
    return &Server{Addr: addr}, nil
}
 
func main() {
    s, err := NewServer("")
    if err != nil {
        fmt.Println("创建失败:", err)
        return
    }
    fmt.Println(s.Addr)
}

初始化可能失败时,返回 (*类型, error),这是 Go 标准的错误处理模式。

带内部初始化

type Cache struct {
    data map[string]string
}
 
func NewCache() *Cache {
    return &Cache{
        data: make(map[string]string),
    }
}
 
func (c *Cache) Set(key, value string) {
    c.data[key] = value
}
 
func main() {
    c := NewCache()
    c.Set("name", "Alice")
}

mapslicechannel 等需要 make 初始化的字段,在构造函数里处理好,避免调用方拿到 nil

多种构造方式

type Logger struct {
    Level  string
    Output string
}
 
func NewLogger() *Logger {
    return &Logger{
        Level:  "info",
        Output: "stdout",
    }
}
 
func NewLoggerWithLevel(level string) *Logger {
    return &Logger{
        Level:  level,
        Output: "stdout",
    }
}

没有函数重载,就用不同名字的 New 函数提供多种构造方式。

Options 模式

type Option func(*Server)
 
type Server struct {
    Addr    string
    Timeout int
    MaxConn int
}
 
func WithTimeout(t int) Option {
    return func(s *Server) {
        s.Timeout = t
    }
}
 
func WithMaxConn(n int) Option {
    return func(s *Server) {
        s.MaxConn = n
    }
}
 
func NewServer(addr string, opts ...Option) *Server {
    s := &Server{
        Addr:    addr,
        Timeout: 30,
        MaxConn: 100,
    }
    for _, opt := range opts {
        opt(s)
    }
    return s
}
 
func main() {
    s := NewServer("localhost", WithTimeout(60), WithMaxConn(200))
}

参数多时用 Options 模式,每个可选参数是一个函数,灵活且可扩展。

常见写法

写法说明
func NewUser() *User基本构造函数
func NewServer(addr string) (*Server, error)带错误返回
func NewConfig(host string) *Config带默认值
func NewCache() *Cache带内部初始化
func NewXxx(opts …Option) *XxxOptions 模式
func NewXxxWithLevel(l string) *Xxx多种构造方式

特点

  • Go 没有构造函数关键字,用 New 开头的普通函数代替
  • 构造函数通常返回指针 *类型
  • 初始化可能失败时返回 (*类型, error)
  • 内部需要 make 的字段(map、channel 等)在构造函数里初始化
  • 没有函数重载,用不同函数名区分多种构造方式
  • Options 模式适合参数多、可选参数多的场景
  • 构造函数是社区约定,不是语言特性,但几乎所有 Go 项目都遵循

理解

Go 用简单的 New 函数替代了其他语言的构造函数,看起来朴素但够用。好处是初始化逻辑就是一个普通函数,容易测试、容易理解。记住在构造函数里处理好默认值和需要 make 的字段,调用方拿到的就是一个随时可用的实例。

引出

构造函数创建实例,方法绑定行为,那怎么让不同类型有统一的”能力描述”?这就是 Go 接口 的作用——定义一组方法签名,任何实现了这些方法的类型都算实现了这个接口。