Go 语言中的 “函数选项模式”

作者: adm 分类: go 发布时间: 2025-03-02

下面我将用通俗易懂的语言,详细介绍 Go 语言中的 “函数选项模式”(Functional Options Pattern),并配合一个完整的、可运行的代码实例,让你一看就懂。

🎯 一、什么是“函数选项模式”?

想象一下:你要开一家咖啡店,顾客点咖啡时可以自定义:

咖啡种类(美式、拿铁)
是否加糖
是否加奶
温度(热/冰)
杯型(小/中/大)

如果用传统方式写代码,你可能会写一堆参数:

Go


NewCoffee("拿铁", true, true, "热", "大")

但这样有几个问题:

参数太多,容易搞混顺序;
很多参数是可选的,但你还得传默认值;
不够灵活,以后加个“加香草糖浆”就得改函数签名。
👉 函数选项模式就是为了解决这些问题而生的!

✅ 二、核心思想(一句话)

用函数来设置配置项,而不是用一堆参数。
这样你就可以像这样创建对象:

Go


coffee := NewCoffee(
    WithType("拿铁"),
    WithSugar(true),
    WithMilk(true),
    WithTemperature("热"),
)

是不是清晰多了?想加什么就加什么,顺序无所谓,还容易扩展!

🧱 三、实现原理(3个关键部分)

我们通过一个 “创建聊天机器人” 的例子来说明。

第一步:定义配置结构体

Go


type Bot struct {
    Name     string
    Age      int
    Greeting string
    Mood     string // 心情:happy, serious
}

第二步:定义“选项”类型

Go


// Option 是一个函数类型,它接收一个 *Bot 并修改它
type Option func(*Bot)

👉 解释:Option 不是一个结构体,而是一个函数类型,它能“操作”Bot 的配置。

第三步:写一些“设置函数”(也就是选项)

Go


func WithName(name string) Option {
    return func(b *Bot) {
        b.Name = name
    }
}

func WithAge(age int) Option {
    return func(b *Bot) {
        b.Age = age
    }
}

func WithGreeting(greeting string) Option {
    return func(b *Bot) {
        b.Greeting = greeting
    }
}

func WithMood(mood string) Option {
    return func(b *Bot) {
        b.Mood = mood
    }
}

👉 每个 WithXXX 函数都返回一个“能修改 Bot”的函数。

第四步:写一个构造函数

Go


func NewBot(opts ...Option) *Bot {
    // 1. 创建默认的 Bot
    bot := &Bot{
        Name:     "小助手",
        Age:      1,
        Greeting: "你好!",
        Mood:     "happy",
    }

    // 2. 应用所有传进来的选项
    for _, opt := range opts {
        opt(bot) // 执行每个选项函数,修改 bot
    }

    return bot
}

🧪 四、完整可运行代码示例

Go


package main

import "fmt"

// 1. 定义结构体
type Bot struct {
    Name     string
    Age      int
    Greeting string
    Mood     string
}

// 2. 定义 Option 类型
type Option func(*Bot)

// 3. 各种 With 函数
func WithName(name string) Option {
    return func(b *Bot) {
        b.Name = name
    }
}

func WithAge(age int) Option {
    return func(b *Bot) {
        b.Age = age
    }
}

func WithGreeting(greeting string) Option {
    return func(b *Bot) {
        b.Greeting = greeting
    }
}

func WithMood(mood string) Option {
    return func(b *Bot) {
        b.Mood = mood
    }
}

// 4. 构造函数
func NewBot(opts ...Option) *Bot {
    bot := &Bot{
        Name:     "小助手",
        Age:      1,
        Greeting: "你好!",
        Mood:     "happy",
    }

    for _, opt := range opts {
        opt(bot)
    }

    return bot
}

// 5. 主函数测试
func main() {
    // 创建一个默认机器人
    bot1 := NewBot()
    fmt.Printf("Bot1: %+v\n", bot1)

    // 创建一个自定义机器人
    bot2 := NewBot(
        WithName("小智"),
        WithAge(3),
        WithGreeting("欢迎使用我们的服务!"),
        WithMood("serious"),
    )
    fmt.Printf("Bot2: %+v\n", bot2)
}

输出结果:


Bot1: &{Name:小助手 Age:1 Greeting:你好! Mood:happy}
Bot2: &{Name:小智 Age:3 Greeting:欢迎使用我们的服务! Mood:serious}

✅ 五、优点总结

优点 说明
✅ 参数清晰 每个选项都有名字,不会搞混
✅ 可选性强 只设置你需要的,其他用默认值
✅ 易于扩展 想加新功能?加个 WithXXX 函数就行
✅ 向后兼容 不用改老代码,不影响已有调用
🚫 六、什么时候不要用?

配置项很少(比如就1-2个)——直接传参更简单;
性能要求极高(函数调用有轻微开销);
但大多数情况下,函数选项模式是 Go 中构建复杂对象的推荐方式。

然后构造函数中遍历并执行这些函数。

✅ 总结一句话:

函数选项模式 = 用命名函数来设置配置,让构造对象更清晰、更灵活、更 Go 风格。

如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!