构造器模式
编写符合 Go 惯用方式的构造器
构造器模式 是 CoddyKit 上的免费 Go Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Go Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Go Academy 课程共包含 4 节课。
为什么需要构造函数
Go 没有用于类型的 new 关键字,也没有类构造函数。惯例是使用名为 New... 的函数验证输入,并返回已初始化的值或指针。
简单的 New 函数
返回一个指针,以确保调用方使用共享实例:
package main
import "fmt"
type Server struct {
Host string
Port int
}
func NewServer(host string, port int) *Server {
return &Server{Host: host, Port: port}
}
func main() {
s := NewServer("localhost", 8080)
fmt.Println(s.Host, s.Port)
}在构造函数中进行验证
构造函数适合验证输入并返回错误:
package main
import ("errors"; "fmt")
type Config struct{ Workers int }
func NewConfig(workers int) (*Config, error) {
if workers < 1 {
return nil, errors.New("workers must be >= 1")
}
return &Config{Workers: workers}, nil
}
func main() {
c, err := NewConfig(4)
fmt.Println(c, err)
}函数式选项模式
函数式选项模式允许使用可选配置,而不会破坏 API:
package main
import "fmt"
type Server struct{ host string; port int; timeout int }
type Option func(*Server)
func WithPort(p int) Option { return func(s *Server) { s.port = p } }
func WithTimeout(t int) Option { return func(s *Server) { s.timeout = t } }
func NewServer(host string, opts ...Option) *Server {
s := &Server{host: host, port: 8080, timeout: 30}
for _, o := range opts { o(s) }
return s
}
func main() {
s := NewServer("localhost", WithPort(9090), WithTimeout(60))
fmt.Println(s.port, s.timeout)
}构建器模式
对于复杂对象,构建器模式会将设置方法串联起来并返回结果:
package main
import "fmt"
type QueryBuilder struct{ table, where string; limit int }
func (q *QueryBuilder) Table(t string) *QueryBuilder { q.table = t; return q }
func (q *QueryBuilder) Where(w string) *QueryBuilder { q.where = w; return q }
func (q *QueryBuilder) Limit(n int) *QueryBuilder { q.limit = n; return q }
func (q *QueryBuilder) Build() string {
return fmt.Sprintf("SELECT * FROM %s WHERE %s LIMIT %d", q.table, q.where, q.limit)
}
func main() {
sql := (&QueryBuilder{}).Table("users").Where("age>18").Limit(10).Build()
fmt.Println(sql)
}返回值还是指针
当结构体较小且不可变时返回值;当结构体较大、可变或实现接口时返回指针:
- 小型配置结构体 → 值
- 有状态对象(服务器、客户端、池)→ 指针
- 如果使用指针接收者实现接口 → 指针
默认值模式
使用默认值函数或结构体字面量设置合理的默认值,然后再应用选项:
package main
import "fmt"
type Cache struct{ maxSize int; ttlSecs int }
func defaultCache() Cache { return Cache{maxSize: 100, ttlSecs: 300} }
func NewCache(opts ...func(*Cache)) *Cache {
c := defaultCache()
for _, o := range opts { o(&c) }
return &c
}
func main() {
c := NewCache(func(c *Cache) { c.maxSize = 500 })
fmt.Println(c.maxSize, c.ttlSecs) // 500 300
}单例模式
使用 sync.Once 确保共享资源只初始化一次:
package main
import ("fmt"; "sync")
type DB struct{ DSN string }
var (
instance *DB
once sync.Once
)
func GetDB(dsn string) *DB {
once.Do(func() { instance = &DB{DSN: dsn} })
return instance
}
func main() {
db1 := GetDB("postgres://...")
db2 := GetDB("ignored")
fmt.Println(db1 == db2) // true
}测试构造函数
返回错误的构造函数易于测试:
package main
import ("errors"; "fmt")
type Pool struct{ size int }
func NewPool(size int) (*Pool, error) {
if size <= 0 { return nil, errors.New("size must be positive") }
return &Pool{size: size}, nil
}
func main() {
_, err := NewPool(-1)
fmt.Println(err) // size must be positive
p, _ := NewPool(5)
fmt.Println(p.size) // 5
}避免使用 init()
init() 函数会自动运行,难以测试。相比在 init() 中进行隐式初始化,更推荐使用返回错误的显式构造函数。
快速检查
函数式选项模式解决了什么问题?
回顾:构造函数模式
要点:
- 使用
New...函数作为构造函数 - 验证失败时返回错误
- 使用函数式选项实现灵活的可选配置
- 使用构建器模式创建复杂的查询或配置对象
- 相比
init(),优先使用显式构造函数
练习提示
使用函数式选项实现一个 NewHTTPClient 构造函数。支持 WithTimeout、WithBaseURL 和 WithMaxRetries 选项。
常见问题解答
「构造器模式」课时是免费的吗?
是的 — 「构造器模式」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Go Academy 课程的其余内容,请升级到 CoddyKit PRO。 Go Academy 课程共包含 4 节课。
「构造器模式」这节课中我会学到什么?
编写符合 Go 惯用方式的构造器 你通过在浏览器中直接运行的动手代码来练习 Go Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 Go Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 Go Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「构造器模式」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 Go Academy 课中编写并运行代码吗?
能。每节 Go Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。