外观模式在Go中通过组合实现:定义结构体嵌入接口类型依赖,构造函数注入依赖并返回错误,支持延迟初始化与测试mock;适用于调用链深、模块耦合紧、测试依赖多的场景。
外观模式在Go里不是靠继承,而是靠组合
Go没有类和继承,所以没法像Java那样定义一个 Facade 类去继承一堆子系统。实际做法是定义一个结构体,把需要封装的依赖(比如数据库连接、HTTP客户端、缓存实例)作为字段嵌入,再提供简洁的方法对外暴露。关键在于:这些字段通常是接口类型,便于测试和替换。
- 不要直接持有具体实现(如
*sql.DB),优先用接口(如database/sql包里的driver.Conn或自定义DataStore接口) - 构造函数应接受依赖项,而不是在内部 new,否则无法控制生命周期和 mock
- 如果子系统初始化成本高(如建立连接池),外观结构体本身可带
Init()方法延迟加载
什么时候该用外观模式?看这三种典型场景
不是所有封装都叫外观模式。真正适用的情况有明确信号:
- 调用链超过3层且频繁出现,比如每次处理订单都要手动查用户→查库存→调支付→写日志→发消息
- 多个包/模块边界模糊,外部代码不得不 import 七八个内部包才能完成一个业务动作
- 测试时要 mock 五六个依赖才跑得通单元测试,说明职责没聚拢
反例:只把一个函数简单包装成另一个函数(如 func GetUser(id int) { return db.GetUser(id) }),这不是外观,只是别名。
NewOrderService 这类构造函数容易漏掉错误处理
外观结构体的初始化函数常被写成无返回错误的版本,但实际中依赖可能失败——比如 Redis 连接超时、配置缺失、证书校验失败。不暴露错误会导致 panic 或静默降级。