设计模式在实际中的应用—责任链/适配器
一 何为责任链模式/适配器模式?
这类定义很多人都写了,这里不赘述,放上我随便找的链接:
简单总结一下我对这两种设计模式的理解:
1 责任链模式,需要有一个指向下一个bean的指针或成员变量,在当前方法执行到某个条件判断时,就会调用下一个bean的方法;构建好责任链后,只需调用一个链的一个bean的方法,就会按情况自动执行后续链;
2 适配器模式:加入A,B,C,D四个bean都是处理日志的组件,但是其提供的接口名、入参、出参可能都不一样,如何用一个接口统筹所有组件的相同功能? -----》适配器模式;
二 实际应用实例-----邮件/短信发送功能
1 业务分析
现在很多app都需要给用户发送邮件/短信,场景包括:
登录验证码、修改密码、推送广告等等,谁还不是个每天都被推送骚扰的打工人呢?
所以一个app,肯定会需要对接国内外的邮件、短信服务商;
而且不能只有一家,万一他家更新服务,或者网络波动等等,自己app的发送功能必然收到影响,邮件、短信系统的可靠性就降低了;
同时,你需要知道自己的消息是不是发成功了,如果失败了,在某些场景下,比如交易时,那么就需要重新发送了。因此这个系统还需要可以获取发送结果;
总结一下:
1 需要对接多家服务商;
2 需要获取发送结果;
简单分析下,不难得出:
多家服务商保证可靠性----》责任链;
获取结果----》适配器模式
2 代码示例:
这里以邮件为例。
首先,定义邮件接口:
public interface EmailComponent {
/**
* 获取发送邮件结果
*
* @param emailId 各服务平台邮件唯一标识ID
* @return 邮件发送结果
*/
SendResult getSendResult(String emailId);
/**
* 发送文本邮件
* @param dto 发送文本邮件dto
*/
void sendTextEmail(TextEmailDTO dto);
/**
* 构建责任链, 用于重试失败自动选择其他服务商
*
* @param component 邮件组件
*/
void setNextProvider(TextEmailComponent component);
}
public class TextEmailDTO {
/**
* 接收人
*/
private String receiver;
/**
* 消息内容
*/
private String content;
/**
* 邮件标题/摘要
*/
private String subject;
}
服务商A:
public class A implements EmailComponent{
/**
* 下一个提供商
*/
private EmailComponent nextProvider;
@Override
public void sendTextEmail(TextEmailDTO dto){
Boolean sendResult = doSendEmail(dto);
if(sendResult) {
record(); //插入记录
}else {
nextProvider.sendTextEmail(dto);
}
}
@Override
public void setNextProvider(TextEmailComponent component) {
this.nextEmailComponent = component; //用于构建责任链
}
@Override
public SendResult getSendResult(String emailId) {
SendResultOfA resultOfA = getSendResultOfA(emailId); //每个服务上返回的结果都是不一样的;如果用了服务B,这里就会是SendResultOfB
return constructSendResult(resultOfA); //构造统一的返回对象
}
private SendResult constructSendResult(MailGunSendResult mailGunResult) {
...... //针对不同厂商,方法内容不同
}
}
从服务A的代码可以看出:
用成员变量nextProvider 来指向下一个服务商,完成责任链的调用;
用 getSendResult(String emailId) 方法,将不同的厂商获取发送结果的方法统一,调用者只需要调用 getSendResult(String emailId) 方法即可,无须再知道每个厂商的具体实现细节,即可获取到发送结果;
责任链如何构建,这里给个思路:
1 先在spring容器中拿到所有服务商bean,然后根据想要的顺序排序后存入list中;
2 将list(0)的下一个服务商设置为list(1), 以此类推;
3 注意:当list索引为list.size()-1 时,已经是责任链的末尾了,此时需要将该服务商的下级服务商设置为null;否则,当再次构建服务商责任链时,可能会形成一个环。比如A->B->C,即A服务商最先被调用,失败则调用B,以此类推;此时你想把顺序换为A, C, B, 即A->C->B, 此时,若不设置B的下级为null,由于顺序改变前B指向C, 那么改变后,就变成了A->C->B->C->B…一直BC循环;