17370845950

如何使用Golang实现外观模式_Go外观模式封装思路
Go中外观模式用结构体聚合接口依赖实现,NewOrderFacade传入所有必需服务,方法透传错误并保留因果链,禁止硬编码子系统逻辑或延迟初始化。

Go 语言没有类和继承,所以外观模式(Facade Pattern)不能照搬面向对象教科书里的写法;它本质是「用一个统一接口封装一组复杂子系统」,在 Go 里直接用结构体 + 方法组合就能干净实现,关键在于职责隔离和导出控制。

struct 封装多个子服务实例

外观的核心是聚合子系统并提供简化入口。不要把子系统逻辑塞进外观方法里,而是通过字段持有它们的实例——这样便于测试替换、依赖注入,也符合 Go 的组合哲学。

常见错误:把所有子系统逻辑硬编码在外观方法内部,导致无法单独测试或 mock。

  • 子系统类型应定义为接口(如 UserServiceNotificationService),外观只依赖接口
  • 外观结构体字段用小写首字母(如 userService),避免意外导出
  • 构造函数(如 NewOrderFacade)负责组装,调用方只关心这个工厂函数

NewOrderFacade 必须接收所有依赖,禁止延迟初始化

外观不是单例工具类,它的行为强依赖底层服务状态。如果在方法里才去 new 子服务,会导致不可测、难替换、资源泄漏(比如重复开数据库连接)。

典型反模式:func (f *OrderFacade) PlaceOrder(...) { db := sql.Open(...) } —— 每次都新建连接,且无法注入 mock DB。

  • 所有依赖必须在 NewOrderFacade(userSvc UserService, notifySvc NotificationService, ...) 中传入
  • 若某子系统可选,用指针或 nil 判断,但需在文档中明确其影响(例如通知失败不阻断下单)
  • 构造函数应做最小必要校验(如 if userSvc == nil { panic("userSvc required") }),不建议静默容忍 nil

外观方法要返回具体错误,别吞掉子系统错误

外观不是错误过滤器。用户需要知道是库存不足、支付超时,还是用户不存在——这些信息来自不同子系统,外观只需透传或包装,不能统一转成 "operation failed"

容易踩的坑:用 fmt.Errorf("failed to place order") 抹掉原始错误链,导致线上排查困难。

  • fmt.Errorf("place order: %w", err) 保留错误因果链
  • 对特定错误做语义化包装(如将 sql.ErrNoRows 转为 UserNotFound),但保留原始 error 作为 cause
  • 避免在外观层重试(如自动重试支付),那是子系统或业务策略层的事
type OrderFacade struct {
	userService     UserService
	paymentService  PaymentService
	inventoryService InventoryService
}

func NewOrderFacade(u UserService, p PaymentService, i InventoryService) *OrderFacade {
	return &OrderFacade{
		userService:     u,
		paymentService:  p,
		inventoryService: i,
	}
}

func (f *OrderFacade) PlaceOrder(ctx context.Context, req PlaceOrderRequest) error {
	if _, err := f.userService.GetUser(ctx, req.UserID); err != nil {
		return fmt.Errorf("get user: %w", err)
	}
	if err := f.inventoryService.Reserve(ctx, req.Items); err != nil {
		return fmt.Errorf("reserve inventory: %w", err)
	}
	if err := f.paymentService.Charge(ctx, req.Payment); err != nil {
		return fmt.Errorf("charge payment: %w", err)
	}
	return nil
}

真正难的是边界划分:哪些逻辑该放进子系统,哪些留在外观?比如「库存扣减失败时是否自动回滚用户账户余额」——这已经超出外观职责,属于事务

协调层。外观只管顺序调用+错误传播,别越界。