Go工厂模式实现全景图
为什么需要工厂模式?
问题场景
假设没有工厂模式:
// 直接根据任务类型硬编码资源生成
func generateResources(taskType string) Resources {
switch taskType {
case "k8s-job":
// 300行生成Job的代码
case "spark":
// 500行处理Spark提交的逻辑
case "flink":
// 另一个逻辑分支...
}
}
痛点:
- 代码臃肿(单个函数上千行)
- 修改风险高(牵一发而动全身)
- 无法复用校验逻辑
工厂模式优势
优势 | 说明 | 业务价值 |
---|---|---|
隔离变化 | 新增任务类型只需添加新工厂类 | 提升扩展性 |
逻辑复用 | 校验方法可被所有工厂复用 | 减少重复代码 |
职责清晰 | 每个工厂只关注自己的领域 | 提升可维护性 |
易于测试 | 可对单个工厂Mock测试 | 提高质量 |
一、核心组件定义
1. 工厂接口(契约)
// job/factory.go
type JobFactory interface {
Validate(ctx context.Context) error // 参数校验
GenerateResources(ctx context.Context) // 资源生成
GetRuntimeConfig() map[string]interface{} // 获取配置
}
2. 基础结构体(公共能力)
// job/base_factory.go
type BaseFactory struct {
task *TaskInfo // 公共字段
subtasks []*SubTaskInfo
logger Logger // 公共依赖
metrics MetricsCollector
}
// 公共方法实现
func (b *BaseFactory) validateCommon() error {
if len(b.subtasks) == 0 {
return ErrEmptyTasks
}
return nil
}
3. 具体工厂(业务实现)
// job/k8s_factory.go
type KubernetesFactory struct {
*BaseFactory // 组合基础能力
kubeClient *k8s.Client // 特有依赖
}
func (k *KubernetesFactory) Validate(ctx context.Context) error {
if err := k.validateCommon(); err != nil { // 复用基础校验
return err
}
if k.task.Namespace == "" {
return ErrMissingNamespace
}
return nil
}
二、隐式接口实现机制
实现原理
var _ JobFactory = (*KubernetesFactory)(nil) // 类型检查
// 编译器自动检查:
1. KubernetesFactory 是否实现所有接口方法?
- Validate() ✔️
- GenerateResources() ✔️
- GetRuntimeConfig() ✔️
2. 全部实现 → 满足接口
与传统语言对比
特性 | Java/C# | Go |
---|---|---|
接口声明 | class A implements B | 无显式声明 |
方法关联 | 显式@Override | 方法签名匹配即实现 |
类型检查 | 编译时强制 | 通过var _检查 |
三、组合模式深度解析
1. 内存结构
k8sFactory := &KubernetesFactory{
BaseFactory: &BaseFactory{...},
kubeClient: client,
}
内存布局:
+-----------------------+
| KubernetesFactory |
| +----------------+ |
| | BaseFactory* |----> 独立的BaseFactory实例
| +----------------+ |
| kubeClient* |----> 指向k8s客户端
+-----------------------+
2. 方法调用链
k8sFactory.validateCommon() 的执行路径:
1. 查找KubernetesFactory是否有该方法 → ❌
2. 查找嵌入的BaseFactory → ✅找到并执行
3. 方法覆盖示例
// 在KubernetesFactory中重写基础方法
func (k *KubernetesFactory) GetRuntimeConfig() map[string]interface{} {
baseConfig := k.BaseFactory.GetRuntimeConfig() // 调用父级
baseConfig["k8s_specific"] = true
return baseConfig
}
四、工厂创建流程
1. 初始化过程
func NewJobFactory(task *TaskInfo) (JobFactory, error) {
base := &BaseFactory{
task: task,
logger: NewLogger(),
metrics: NewMetrics(),
}
switch task.JobType {
case "k8s":
client, err := k8s.NewClient(task.ClusterID)
return &KubernetesFactory{
BaseFactory: base,
kubeClient: client,
}, nil
case "spark":
return &SparkFactory{
BaseFactory: base,
sparkOpts: parseSparkConfig(task),
}, nil
}
}
2. 生命周期示意图
五、设计决策要点
1. 何时使用接口方法 vs 结构体方法
场景 | 推荐方式 | 示例 |
---|---|---|
需要多态调用 | 接口方法 | factory.Validate() |
内部工具方法 | 结构体私有方法 | (k *K8sFactory) initDeployment() |
跨工厂共享逻辑 | BaseFactory公共方法 | base.validateCommon() |
2. 组合模式最佳实践
- 避免深层嵌套:通常只组合1-2层
- 明确职责边界:
// 不好的实践:模糊的责任划分 type K8sFactory struct { *BaseFactory *NetworkManager *StorageHelper } // 好的实践:清晰单一职责 type K8sFactory struct { *BaseFactory k8sResources *ResourceBuilder // 聚合子模块 }
3. 初始化复杂度管理
// 复杂初始化使用Option模式
type FactoryOption func(*BaseFactory)
func WithCustomLogger(l Logger) FactoryOption {
return func(b *BaseFactory) {
b.logger = l
}
}
func NewBaseFactory(opts ...FactoryOption) *BaseFactory {
b := &BaseFactory{...} // 默认值
for _, opt := range opts {
opt(b)
}
return b
}
六、性能优化技巧
1. 对象池复用
var factoryPool = sync.Pool{
New: func() interface{} {
return &KubernetesFactory{
BaseFactory: &BaseFactory{...},
}
},
}
func GetFactory() *KubernetesFactory {
return factoryPool.Get().(*KubernetesFactory)
}
func PutFactory(f *KubernetesFactory) {
f.ResetState()
factoryPool.Put(f)
}
2. 并行资源生成
func (k *KubernetesFactory) GenerateResources(ctx context.Context) error {
var wg sync.WaitGroup
errChan := make(chan error, 2)
// 并行生成Deployment
wg.Add(1)
go func() {
defer wg.Done()
_, err := k.createDeployment()
errChan <- err
}()
// 并行生成Service
wg.Add(1)
go func() {
defer wg.Done()
_, err := k.createService()
errChan <- err
}()
go func() {
wg.Wait()
close(errChan)
}()
for err := range errChan {
if err != nil {
return err
}
}
return nil
}
七、测试策略
1. 接口Mock测试
type MockFactory struct {
ValidateFunc func(ctx context.Context) error
}
func (m *MockFactory) Validate(ctx context.Context) error {
return m.ValidateFunc(ctx)
}
func TestHandler(t *testing.T) {
mock := &MockFactory{
ValidateFunc: func(ctx context.Context) error {
return errors.New("test error")
}
}
handler := NewHandler(mock)
err := handler.Process()
assert.Error(t, err)
}
2. 表格驱动测试
func TestK8sFactory_Validate(t *testing.T) {
tests := []struct {
name string
task *TaskInfo
wantErr bool
}{
{
name: "valid task",
task: &TaskInfo{Namespace: "default"},
wantErr: false,
},
{
name: "empty namespace",
task: &TaskInfo{Namespace: ""},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
f := &KubernetesFactory{
BaseFactory: &BaseFactory{task: tt.task},
}
err := f.Validate(context.Background())
assert.Equal(t, tt.wantErr, err != nil)
})
}
}
八、扩展与演进
1. 新增工厂类型流程
2. 典型扩展示例
// 新增Flink任务支持
type FlinkFactory struct {
*BaseFactory
flinkConfig FlinkConfig
}
func (f *FlinkFactory) GenerateResources() error {
// 实现Flink集群部署逻辑
}
// 更新工厂创建器
func NewJobFactory(task *TaskInfo) (JobFactory, error) {
// ...
case "flink":
return &FlinkFactory{
BaseFactory: base,
flinkConfig: parseFlinkConfig(task),
}, nil
}
通过这种模式,你的作业系统可以获得:
- 横向扩展能力:新增任务类型不影响现有逻辑
- 纵向复用能力:公共逻辑集中在BaseFactory
- 清晰的架构边界:各工厂类职责单一
- 可测试性:接口便于Mock测试
关键要记住:Go的工厂模式是通过 接口定义行为 + 结构体组合复用 + 隐式实现验证 这三大支柱实现的,这与传统OOP语言的实现有本质区别。