Factory Method 和 Strategy 是微服务中刚需设计模式:前者通过工厂函数封装多实现组件的创建逻辑,解耦构造细节并支持 mock;后者用 Supports/Execute/Priority 方法实现运行时动态策略决策,避免硬编码分支。

Factory Method 和 Strategy 是 Golang 微服务中最常用、最值得优先落地的两个设计模式——不是因为它们“高级”,而是因为微服务天然存在多实现、多分支、多配置的场景,而这两个模式能直接把耦合点切掉。
为什么工厂模式在微服务里不是“可选”,而是“刚需”
微服务启动时要初始化各种依赖:数据库连接池、消息客户端、HTTP 客户端、缓存驱动……这些组件往往有多个实现(比如 RedisClient / MemcachedClient,或 KafkaProducer / RabbitMQProducer),且初始化参数差异大(超时、重试、TLS 配置等)。
直接写 &RedisClient{Addr: cfg.Addr, Timeout: cfg.Timeout} 会导致:
- 所有调用方都得知道字段名和构造细节,改一个字段就得全量 grep 修改
- 测试时无法替换 mock 实现(比如想测业务逻辑,却绕不开真实 Redis 连接)
- 环境差异化配置(dev/staging/prod)只能靠 if-else 分支,越堆越难维护
正确做法是把创建逻辑收口到工厂函数里:
func NewCacheClient(cfg CacheConfig) (CacheClient, error) {
switch cfg.Type {
case "redis":
return NewRedisClient(cfg.Redis), nil
case "memcache":
return NewMemcacheClient(cfg.Memcache), nil
default:
return nil, fmt.Errorf("unsupported cache type: %s", cfg.Type)
}
}
注意三点:
立即学习“go语言免费学习笔记(深入)”;
- 工厂函数返回接口类型(如
CacheClient),不暴露具体结构体 - 参数用结构体传入(
CacheConfig),避免参数膨胀后加字段破坏签名 - 工厂本身不执行 I/O(比如不在这儿 dial Redis),只做轻量组装;连接建立延迟到
Init()或首次使用时
策略模式解决的是“运行时动态决策”,不是“if-else 拆分”
微服务里大量存在条件路由逻辑:支付渠道选择、风控规则匹配、灰度流量分发。如果用嵌套 if 或 switch 硬编码,每次加一种策略都要改主流程,还容易漏掉 support 判断。
策略模式的核心不是多态,而是“让策略自己说话”:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 每个策略实现
Supports(ctx)方法,由它决定是否参与本次决策 - 主流程只负责排序(按
Priority())、过滤(Supports()返回 true)、调用(Execute()) - 新增策略只需注册新 struct,无需动原有代码
典型错误是把 Supports 写成纯静态判断(比如只看字符串等于),结果策略之间互相覆盖。真正可用的 Supports 应该基于上下文:
func (s *HighAmountAlipayStrategy) Supports(ctx PaymentContext) bool {
return ctx.Amount > 5000 &&
ctx.User.Region == "CN" &&
ctx.User.Level >= 3
}
别忘了加 Priority():当多个策略同时满足时,靠数值小的胜出。否则会随机触发(Go map 遍历无序)。
工厂和策略经常一起用,但职责必须分清
常见混淆是把策略注册也塞进工厂里,比如:
// ❌ 错误:工厂既创建实例,又管理策略生命周期
func NewPaymentStrategyFactory() *StrategyFactory { ... }
这会让单元测试无法单独验证某个策略逻辑。正确分层是:
-
Factory只管“造出来”——返回单个策略实例(如NewAlipayStrategy()) -
StrategyRegistry负责“装进去”——用sync.Map存所有策略,提供FindSuitable()方法 - 业务 handler 只依赖
StrategyRegistry,完全不知道工厂存在
这样测试时可以直接注入 mock registry,或用 registry.Register(&TestStrategy{}) 注册测试策略,不用碰工厂代码。
最容易被忽略的点:策略的 Supports 方法不能有副作用(比如发 HTTP 请求、查 DB),否则会影响主流程性能和可预测性。它应该只是纯计算——输入 context,输出 bool。

















