Go中需用桥接模式解耦消息内容与渠道,定义Notifier接口和Renderer接口,再通过抽象工厂按环境动态创建组合,避免硬编码导致的维护灾难。

为什么不能直接写 SendEmail() 和 SendSMS()
Go 里没有“通知服务”的内置抽象,硬编码多个发送函数会让业务逻辑和渠道强耦合。比如改短信服务商,就得翻出所有调用 SendSMS() 的地方逐个替换;加个站内信?又得补 SendInApp(),再改一遍上层逻辑。这不是扩展,是维护灾难。
真正要解决的,是让“发什么”(内容、优先级、目标用户)和“怎么发”(邮件 SMTP、短信 HTTP API、WebSocket 推送)彻底解耦。核心不是多写几个函数,而是定义清楚谁该负责什么。
- 消息内容结构(
Message)必须独立于渠道,含To、Subject、Body、Priority等通用字段 - 发送行为抽象为接口:
type Notifier interface { Notify(msg Message) error },不暴露任何渠道细节 - 每个渠道实现这个接口,比如
SMTPNotifier、SMSNotifier、InAppNotifier - 业务代码只依赖
Notifier,测试时可轻松注入 mock 实现
桥接模式怎么用在消息组件里
光有 Notifier 接口还不够——当消息需要支持不同格式(Markdown / HTML / 纯文本)+ 不同渠道(邮件 / 短信 / APP Push)时,类爆炸就来了。桥接模式就是为这种二维变化设计的:把“内容渲染”和“渠道发送”拆成两层,用组合连起来。
关键不是继承,是字段嵌入:
立即学习“go语言免费学习笔记(深入)”;
- 定义渲染器接口:
type Renderer interface { Render(msg Message) ([]byte, error) } - 实现具体渲染器:
HTMLRenderer、TextRenderer、MarkdownRenderer - 定义通知器结构体,持有一个
Renderer字段:type SMTPNotifier struct { renderer Renderer } - 构造时传入渲染器:
notifier := &SMTPNotifier{renderer: &HTMLRenderer{}} -
Notify()方法内部先调r.renderer.Render(),再发 HTTP 或 SMTP
这样新增一种格式(比如加个 JSONRenderer),不用动任何通知器;新增一个渠道(比如加个 WebhookNotifier),也不用改渲染逻辑。
抽象工厂怎么配合桥接一起用
当系统需要按环境或配置动态选择整套组合(比如 dev 环境全走 InAppNotifier + TextRenderer,prod 环境走 SMTPNotifier + HTMLRenderer),就不能靠手动 new,得用抽象工厂统一创建。
注意:Go 里抽象工厂不是接口套接口,而是一组构造函数:
- 定义工厂函数签名:
func NewNotifier(env string) Notifier - 内部用
switch env分支,返回不同组合:return &SMTPNotifier{renderer: &HTMLRenderer{}} - 或者更清晰地拆成两个工厂:
NewRenderer(env string) Renderer和NewNotifier(renderer Renderer) Notifier,显式体现桥接关系 - 避免用
interface{}或反射,那会失去类型安全,也难测试
构建标签(//go:build prod)不适合这里——配置应运行时决定,而非编译时硬编码。用 os.Getenv("ENV") 或配置文件更可控。
容易 panic 的三个地方
桥接+工厂组合看着优雅,但 Go 里最容易栽在 nil 指针和生命周期上:
-
Notifier结构体里的renderer字段没初始化就调Render(),直接 panic:所有构造函数必须显式赋值,别留空字段 - 工厂返回的
Notifier里包含 HTTP client 或数据库连接,但没暴露Close()方法,导致资源泄漏:每个可释放组件自己实现Close() error,工厂不负责清理 - 在
Renderer实现里调用了未导出的平台 API(比如 Windows-only 的 COM 接口),却没加构建约束,导致 macOS 构建失败:跨平台逻辑必须严格隔离,import语句不能出现在通用包里
最常被忽略的是错误传播路径——Notify() 返回 error,但上层日志只打 "send failed",根本看不出是渲染失败还是网络超时。建议在 error 中包装上下文,比如用 fmt.Errorf("notify via smtp: %w", err)。


















