Go 中选项模式、单例与策略模式怎么落地?

一句话结论:Go 里谈设计模式要“少谈继承、多用组合与函数”:选项模式(functional options)用“函数即配置”做出可扩展的构造器;单例用 sync.Once 保证线程安全的懒初始化;策略模式用接口抽象行为、运行时替换实现。三者的共同价值是让代码可测、可扩展、少改调用方。

选项模式

type Config struct {
	Timeout time.Duration
	Retries int
}

type Option func(*Config)

func WithTimeout(d time.Duration) Option { return func(c *Config) { c.Timeout = d } }
func WithRetries(n int) Option          { return func(c *Config) { c.Retries = n } }

func NewClient(opts ...Option) *Client {
	cfg := Config{Timeout: 5 * time.Second} // 默认值
	for _, opt := range opts {              // 按序应用
		opt(&cfg)
	}
	return &Client{cfg: cfg}
}

c := NewClient(WithTimeout(time.Second), WithRetries(3))

优点:加配置项不改函数签名、调用方可读性好、天然支持默认值——比一长串参数和“每加一个字段就改构造器”更易维护。

单例模式

var (
	once   sync.Once
	client *Client
)

func GetClient() *Client {
	once.Do(func() {
		client = NewClient() // 只执行一次,且线程安全
	})
	return client
}
  • sync.Once 保证并发下只初始化一次,比双检锁简洁得多。
  • 注意单例是全局可变状态,测试难隔离;必要时提供显式注入或测试替身入口。
  • 简单场景也可用包级 var 直接初始化,代价是进程启动即创建。

策略模式

type Notifier interface {
	Notify(to, msg string) error
}

type EmailNotifier struct{}
func (EmailNotifier) Notify(to, msg string) error { return nil }

type SmsNotifier struct{}
func (SmsNotifier) Notify(to, msg string) error { return nil }

func Send(n Notifier, to, msg string) error {
	return n.Notify(to, msg) // 换 Email/Sms 都行,调用方不变
}

策略模式在 Go 里几乎等于“接口加注入”,常配合注册表 map[string]Notifier 消除大段 switch。

常见追问 / 记忆点

  • 追问:什么时候不该用接口/策略?——只有一个实现且短期不变时,先用具体类型,避免过度设计。
  • 追问:单例与 sync.Once 还会用在哪?——注册表、默认编码器、一次性资源初始化等。
  • 记忆点:选项 = 函数改配置;单例 = Once;策略 = 接口注入;模式是手段,可测可维护才是目的。
笔记加载中…