基于策略模式实现灵活可扩展的短信服务架构
引言
在企业级应用开发中,短信服务是不可或缺的基础功能之一。随着业务发展,我们可能需要接入多个短信服务提供商(如阿里云、腾讯云、第三方短信网关等),并能够在不修改核心业务代码的情况下灵活切换。本文将介绍如何使用策略模式设计一个高扩展性的短信服务架构,并结合实际代码示例进行讲解。
策略模式简介
策略模式(Strategy Pattern)是一种行为设计模式,它定义了一系列算法,并将每个算法封装起来,使它们可以互相替换。策略模式让算法的变化独立于使用算法的客户端。
策略模式的三大角色
- 策略接口(Strategy Interface):定义所有支持的算法的公共接口
- 具体策略(Concrete Strategies):实现策略接口的具体算法类
- 上下文(Context):持有一个策略对象的引用,并将客户端请求委托给策略对象
短信服务架构设计
1. 策略接口设计
java
@Service
public interface SmsComInterFace {
public R sendSms(String phoneNumber, Map<String, Object> templateParams);
}
这里使用了Spring的@Service
注解标记接口,虽然对接口使用@Service
不是必须的,但在某些框架中可以辅助组件扫描。
2. 具体策略实现
以阿里云短信服务为例,实现具体的策略:
java
package com.ruoyi.sms.config;
import com.alibaba.fastjson.JSON;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsRequest;
import com.aliyuncs.dysmsapi.model.v20170525.SendSmsResponse;
import com.aliyuncs.exceptions.ClientException;
import com.aliyuncs.profile.DefaultProfile;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.sms.inter.SmsComInterFace;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.Map;
@Component
public class AliyunSmsUtil implements SmsComInterFace {
@Value("${aliyun.sms.sms-access-key-id}")
private String accessKeyId;
@Value("${aliyun.sms.sms-access-key-secret}")
private String accessKeySecret;
@Value("${aliyun.sms.sms-sign-nam}")
private String signName;
@Value("${aliyun.sms.sms-template-code}")
private String templateCode;
@Value("${aliyun.sms.sms-endpoint}")
private String endpoint;
@Value("${aliyun.sms.region-id}")
private String regionId;
public R sendSms(String phoneNumber, Map<String, Object> templateParams) {
try {
DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, accessKeySecret);
IAcsClient client = new DefaultAcsClient(profile);
SendSmsRequest request = new SendSmsRequest();
request.setPhoneNumbers(phoneNumber);
request.setSignName(signName);
request.setTemplateCode(templateCode);
// 将HashMap转化为JSON字符串
String templateParam = JSON.toJSONString(templateParams);
request.setTemplateParam(templateParam);
SendSmsResponse response = client.getAcsResponse(request);
if(response.getCode() != null && response.getCode().equals("OK")){
return R.ok();
}
return R.fail(response.getMessage());
} catch (ClientException e) {
e.printStackTrace();
return null;
}
}
}
3. 策略工厂与上下文
策略工厂负责管理和提供具体的策略实现:
java
package com.ruoyi.sms.handler;
import com.ruoyi.sms.config.AliyunSmsUtil;
import com.ruoyi.system.api.constants.SmsType;
import com.ruoyi.sms.inter.SmsComInterFace;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Description:
* @author: zh
* @Create : 2025/4/30
* @Project_name : RuoYi-Cloud
* @Version :
**/
@Component
@Slf4j
public class SmsTypeFactory {
// 短信方式常量
private static Map<String, SmsComInterFace> map = new ConcurrentHashMap<>();
//上下文
@Autowired
private ApplicationContext applicationContext;
@PostConstruct
public void init()
if(map.isEmpty()){
//从上下文中找到所有实现了SmsComInterFace接口的类,并注册
Map<String, SmsComInterFace> beansOfType = applicationContext.getBeansOfType(SmsComInterFace.class);
beansOfType.forEach((k,v)->{
map.put(k,v);
});
}
}
/**
* 短信方式
* @param type 传入短信方式
* @return
*/
public static SmsComInterFace getSms(String type) {
SmsComInterFace sms = null;
if(map.containsKey(type)){
sms = map.get(type);
}
if (sms == null) {
throw new NullPointerException("方式选择错误");
}
return sms;
}
}
策略模式的应用
在实际业务中使用短信服务:
java
@RestController
@RequestMapping("/sms")
public class SmsController {
@GetMapping("/send/{phone}")
public R sendSms(@RequestParam Map<String, Object> params,
@PathVariable String phone,
@RequestParam String type) {
try {
SmsComInterFace sms = SmsTypeFactory.getSms(type);
return sms.sendSms(phone, params);
} catch (IllegalArgumentException e) {
return R.fail(e.getMessage());
}
}
}
策略模式的扩展性
当需要新增短信服务提供商时,只需:
- 实现
SmsComInterFace
接口 - 在工厂类中注册新策略
- 无需修改现有代码
例如新增腾讯云短信服务:
java
@Component
public class TencentSmsUtil implements SmsComInterFace {
// 腾讯云实现...
}
//添加上Type
public class SmsType {
public static final String TENCENT_SMS = "tencentSmsUtil";
}
策略模式的优势
-
开闭原则:无需修改现有代码即可扩展新策略
-
消除条件语句:避免大量的if-else或switch-case判断
-
易于测试:每个策略可以单独测试
-
运行时切换:可以根据配置动态切换策略
总结
通过策略模式设计短信服务架构,我们实现了:
- 多种短信服务的统一接入
- 业务代码与具体实现的解耦
- 灵活的策略扩展能力
- 便于维护和测试的代码结构
这种设计不仅适用于短信服务,也可以推广到支付网关、文件存储等需要支持多实现的场景。策略模式是保持软件扩展性和维护性的重要工具之一。