《用户通知体系全流程实战》
第4章 通知渠道适配器模式

4、通知渠道适配器模式:适配器设计模式、各渠道(邮件、短信、推送)适配器实现

在用户通知体系中,不同的通知渠道(邮件、短信、App推送)拥有各自独立的API和协议。为了统一调用方式、降低系统耦合度,我们引入适配器设计模式。适配器模式如同一个“万能插头转换器”,将不同渠道的差异接口转换为统一的INotificationChannel接口,让上层业务代码无需关心底层实现细节。

📐 适配器设计模式核心概念

🎯 适配器模式三要素

  • 🔹 目标接口(Target) — 统一的通知发送接口
  • 🔹 适配者(Adaptee) — 各渠道原生SDK/API
  • 🔹 适配器(Adapter) — 将Adaptee转换为Target

⚙️ 适配器模式优势

  • ✅ 开闭原则 — 新增渠道无需修改现有代码
  • ✅ 单一职责 — 每个适配器只处理一种渠道
  • ✅ 统一调用 — 业务层只需面向接口编程
通知渠道适配器模式结构图 «interface» INotificationChannel BaseNotificationAdapter EmailAdapter (适配 JavaMail / SendGrid) SmsAdapter (适配 阿里云 / Twilio) PushAdapter (适配 极光 / Firebase) 业务层统一调用 send(NotificationRequest) → 适配器转发至各渠道

📧 邮件渠道适配器实现

📌 适配目标:将 JavaMail / SendGrid 等邮件SDK统一为 INotificationChannel

// 目标接口
public interface INotificationChannel {
    boolean send(NotificationRequest request);
    ChannelType getType();
}

// 邮件适配器
public class EmailAdapter implements INotificationChannel {
    private final JavaMailSender mailSender;  // 适配者

    public EmailAdapter(JavaMailSender mailSender) {
        this.mailSender = mailSender;
    }

    @Override
    public boolean send(NotificationRequest request) {
        try {
            MimeMessage message = mailSender.createMimeMessage();
            MimeMessageHelper helper = new MimeMessageHelper(message, true);
            helper.setTo(request.getRecipient());
            helper.setSubject(request.getTitle());
            helper.setText(request.getContent(), true);  // 支持HTML
            mailSender.send(message);
            return true;
        } catch (Exception e) {
            log.error("邮件发送失败: {}", e.getMessage());
            return false;
        }
    }

    @Override
    public ChannelType getType() {
        return ChannelType.EMAIL;
    }
}

📱 短信渠道适配器实现

📌 适配目标:将阿里云短信 / Twilio 等短信SDK统一为 INotificationChannel

// 短信适配器
public class SmsAdapter implements INotificationChannel {
    private final AliyunSmsClient smsClient;  // 适配者

    public SmsAdapter(AliyunSmsClient smsClient) {
        this.smsClient = smsClient;
    }

    @Override
    public boolean send(NotificationRequest request) {
        try {
            SendSmsRequest smsReq = new SendSmsRequest()
                .setPhoneNumbers(request.getRecipient())
                .setSignName("通知系统")
                .setTemplateCode("SMS_123456")
                .setTemplateParam("{\"code\":\"" + request.getContent() + "\"}");
            SendSmsResponse response = smsClient.sendSms(smsReq);
            return "OK".equals(response.getCode());
        } catch (Exception e) {
            log.error("短信发送失败: {}", e.getMessage());
            return false;
        }
    }

    @Override
    public ChannelType getType() {
        return ChannelType.SMS;
    }
}

🔔 App推送渠道适配器实现

📌 适配目标:将极光推送 / Firebase Cloud Messaging 统一为 INotificationChannel

// 推送适配器
public class PushAdapter implements INotificationChannel {
    private final JPushClient jpushClient;  // 适配者

    public PushAdapter(JPushClient jpushClient) {
        this.jpushClient = jpushClient;
    }

    @Override
    public boolean send(NotificationRequest request) {
        try {
            PushPayload payload = PushPayload.newBuilder()
                .setPlatform(Platform.all())
                .setAudience(Audience.alias(request.getRecipient()))
                .setNotification(Notification.newBuilder()
                    .setAlert(request.getTitle())
                    .addAndroidNotification(request.getTitle(), request.getContent(), null)
                    .addIosNotification(request.getTitle(), request.getContent())
                    .build())
                .build();
            PushResult result = jpushClient.sendPush(payload);
            return result.getStatusCode() == 200;
        } catch (Exception e) {
            log.error("推送发送失败: {}", e.getMessage());
            return false;
        }
    }

    @Override
    public ChannelType getType() {
        return ChannelType.PUSH;
    }
}
适配器调用时序流程 业务层 适配器工厂 具体适配器 第三方SDK 1. 获取适配器 2. 调用send() 3. 原生API 4. 返回发送结果 业务层通过工厂获取适配器 → 调用统一send() → 适配器转发至各渠道SDK 📧 邮件 📱 短信 🔔 推送

📊 各渠道适配器对比

特性 📧 邮件适配器 📱 短信适配器 🔔 推送适配器
适配对象 JavaMail / SendGrid 阿里云 / Twilio 极光 / Firebase
内容格式 HTML / 纯文本 纯文本(模板) JSON / 富文本
可靠性 ⭐ 高(异步+重试) ⭐⭐ 中(运营商限制) ⭐⭐⭐ 中高(离线消息)
典型场景 账单、周报、营销 验证码、告警 即时消息、活动

🧩 适配器工厂与统一调用

// 适配器工厂 - 统一管理所有渠道适配器
@Component
public class NotificationAdapterFactory {
    private final Map<ChannelType, INotificationChannel> adapterMap = new HashMap<>();

    public NotificationAdapterFactory(
            EmailAdapter emailAdapter,
            SmsAdapter smsAdapter,
            PushAdapter pushAdapter) {
        adapterMap.put(ChannelType.EMAIL, emailAdapter);
        adapterMap.put(ChannelType.SMS, smsAdapter);
        adapterMap.put(ChannelType.PUSH, pushAdapter);
    }

    public INotificationChannel getAdapter(ChannelType type) {
        INotificationChannel adapter = adapterMap.get(type);
        if (adapter == null) {
            throw new IllegalArgumentException("不支持的渠道类型: " + type);
        }
        return adapter;
    }
}

// 业务层统一调用
@Service
public class NotificationService {
    @Autowired
    private NotificationAdapterFactory adapterFactory;

    public boolean sendNotification(NotificationRequest request) {
        INotificationChannel adapter = adapterFactory.getAdapter(request.getChannelType());
        return adapter.send(request);
    }
}

💡 设计要点总结

  • 每个渠道适配器只负责协议转换,不包含业务逻辑
  • 适配器内部可加入重试、熔断、日志等横切关注点
  • 新增渠道(如模板消息)只需新增一个Adapter实现类
  • 适配器模式让通知系统具备良好的扩展性和可维护性
✨ 专注课程资料整理