关于这三种设计模式是什么这里就不再赘述,本篇文章主要讲的是应用,不理解概念的可以先看一下我的其他关于讲解设计模式的文章,接下来直接进入主题。

书接上文讲的策略+简单工厂模式,本次我们又引入了模板方法,将一些公用的方法定义在抽象类的方法中,不同子类各自实现各自的逻辑。

我们这次还是根据不同的渠道类型进行通知客户(渠道类型有短信sms、邮件email、钉钉dingTalk)

首先就是要有策略接口,接口里面要有一个方法就是通知的方法

public interface PushChannelStrategy{
   SendResult send(MessagePushParam param,BaseMsg baseMsg);
}
  • 1.
  • 2.
  • 3.

然后是抽象类,抽象类中定义发送的通用流程,各自具体发送流程下沉到各自实现类

@Component
public abstract class AbstractPushChannelStrategy implements PushChannelStrategy{
    @Override
    public SendResult send(MessagePushParam parma,BaseMsg baseMsg){
       // 获取目标地址,各自实现类来获取receiveAddress
       ReceiverResult receiverResult =getReceiverAddress(param);
       // 拿到通知的地址,即手机号、邮箱、钉钉id
       String receiverAddress=receiverResult.getReceiverAddress();
       // 非空校验
       if(StringUtils.isBlank(receiverAddress)){
          return new SendResult(MessageStatusEnum.SEND_ADDRESS_NOT_EXIST,param.getReceiverId(),receiverAddress);
       }
       // 开始发送(短信、邮件、钉钉),各自实现发送逻辑
       return send(param,receiverAddress,baseMsg);
   }

   //获取目标地址
   public abstract ReceiverResult getReceiverAddress(MessagePushParam param);
   
   // 具体发送逻辑
   public abstract SendResult send(MessagePushParam param,String targetId,BaseMsg BaseMsg);
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.

然后三个子类短信、邮件、钉钉

@Slf4j
@Component
public class SmsPushChannelStrategy extends AbstractPushChannelStrategy{
   @Autowired
   private NoticeClient noticeClient;
   @Autowired
   private CustomerGateway customerGateway;
   
   @Override
   public SendResult send(MessagePushParam param,String targetId,BaseMsg baseMsg){
       //1、send sms
       if(baseMsg instanceof Sms){
          Sms smsMsg=(Sms)baseMsg;
          NoticeResult noticeResult=noticeClient.sendSms(xxxx);
          SendResult sendResult=new SendResult();
          sendResult.setMessageStatus(MessageStatusEnum.SUCCESS);
          return sendResult;
        }
      throw new MessagePushException("渠道不支持该类型消息");
   }
   
   // 短信通知获取需要通知的手机号
   @Override
   public ReceiverResult getReceiverAddress(MessagePushParam param){
     if(param==null){
       return ReceiverResult.builder().status(MessageStatusEnum.SMS_QUERY_PARAM_NULL).build();
     }
     return customerGateway.getSmsResultByCustId(param.getCustomerId());
 }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

邮件:

@Slf4j
@Component
public class EmailPushChannelStrategy extends AbstractPushChannelStrategy{
   @Autowired
   private NoticeClient noticeClient;
   @Autowired
   private CustomerGateway customerGateway;
   @Override
   public SendResult send(MessagePushParam param,String targetId,BaseMsg baseMsg){
       //1、send email
       if(baseMsg instanceof Email){
         
          Email emailMsg=(Email)baseMsg;
          NoticeResult noticeResult=noticeClient.sendEmail(xxxx);
          sendResult.setMessageStatus(MessageStatusEnum.SUCCESS);
          return sendResult;
       }
      throw new MessagePushException(xxx);
   }
   
   @Override
   public ReceiverResult getReceiverAddress(MessagePushParam param){
     if(param==null){
        return ReceiverResult.builder().status(xxx).build();
      }
     return customerGateway.getEmailResultByCustId(param.getCustomerId());
}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.

钉钉:

@Slf4j
@Component
public class DingTalkPushChannelStrategy extends AbstractPushChannelStrategy{
   @Autowired
   private DingTalkClient dingTalkClient;
   @Autowired
   private SalesGateway salesGateway;
   
   @Override
   public SendResult send(MessagePushParam param,BaseMsg baseMsg){
       //1、send ding talk
       SendResult sendResult=dingTalkClient.send(xxx);
       sendResult.setReceiverId(param.getSalesId());
       // 其他结果参数组装
       return sendResult;
   }
   
   // 根据销售id获取钉钉id
   @Override
   public ReceiverResult getReceiverAddress(MessagePushParam param){
      DingTalkResult dingTalkResult=salesGateway.getDingTalkResultBySalesId(param.getSalesId());
      if(dingTalkResult==null){
         return ReceiverResult.builder().status(xxx).receiverAddress(null).build();
       }
      return ReceiverResult.builder().status(MessageStatusEnum.SUCCESS).receiverAddress(dingTalkResult.getDingTalkId()).build();
}
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.

然后通过策略工厂来获取具体的策略类:(由于只有三个策略类,所以通过注入的方式对channel进行判断)

@Component
public class PushChannelStrategyFactory{
    @Autowired 
    private DingTalkPushChannelStrategy dingTalkPushChannelStrategy;
    @Autowired
    private SmsPushChannelStrategy smsPushChannelStrategy;
    @Autowired
    private EmailPushChannelStrategy emailPushChannelStrategy;
    
    public PushChannelStrategy getStrategy(PushChannel pushChannel){
        switch(pushChannel){
           case DING_TALK:
              return dingTalkPushChannelStrategy;
           case SMS:
              return smsPushChannelStrategy;
           case EMAIL:
              return emailPushChannelStrategy;
           default:
              throw new RuntimeException("不支持的类型");
           }
     }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.

当然策略工厂针对策略实现类比较少的情况还可以这样写:

@Component
public class PushChannelStrategyFactory2{
    @Autowired
    private DingTalkPushChannelStrategy dingTalkPushChannelStrategy;
    @Autowired
    private SmsPushChannelStrategy smsPushChannelStrategy;
    @Autowired
    private EmailPushChannelStrategy emailPushChannelStrategy;
    
    private static final Map<PushChannel,PushChannelStrategy> pushChannelBuilderMap=new HashMap<>();
    
    @PostConstruct
    public void init(){
        pushChannelBuilderMap.put(PushChannel.SMS,smsPushChannelStrategy);
        pushChannelBuilderMap.put(PushChannel.Email,emailPushChannelStrategy);
        pushChannelBuilderMap.put(PushChannel.DING_TALK,dingTalkPushChannelStrategy);
      }

   Public PushChannelStrategy getStrategy(PushChannel PushChannel){
      if(PushChannel==null){
          return null;
       }
       return pushChannelBuilderMap.get(PushChannel);
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.

枚举类:

@Getter
public enum PushChannel{
   SMS("sms","短信"),
   EMAIL("email","邮件"),
   DING_TALK("dingTalk","钉钉");
   
   private final String value;
   
   PushChannel(String value,String desc){
      this.value=value;
    }

   public static PushChannel getPushChannel(String pushChannel){
      if(pushChannel==null){
         return null;
      }
      for(PushChannel channel:PushChannel.values()){
        if(pushChannel.equals(channel.getValue())){
           return channel;
       }
   }
    return null;
  }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.

在使用的时候通过策略工厂里面的方法获取具体的策略类:

@Slf4j
@Service
 public class MessagePushService{
     @Autowired
     private PushChannelStrategyFactory pushChannelStrategyFactory;
     @Autowired
     private MessageRecordRepository messageRecordRepository;
     
     public ResultDTO<Boolean> pushSync(MessagePushCommand command){
        MessagePushParam messagePushParam =MessagePushAssembler.convert(command);
        //1,业务逻辑处理
        //2、根据渠道进行触达
        PushChannel pushChannel=messagePushParam.getChannel();
        if(pushChannel==null){
          throw new MessagePushException(xxx);
        }
        //3、获取具体的策略类
        PushChannelStrategy PushChannelStrategy=pushChannelStrategyFactory.getStrategy(pushChannel);
        SendResult sendResult=PushChannelStrategy.send(messagePushParam,xxx);
        //4,记录落库
        return ResultDTO.getSuccessResult(true);
    }
 }
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.

到此就结束了,是不是比较简单,该案例为实际项目中用到的实际案例,并且这三种设计模式也是比较简单和比较常用的设计模式,欢迎各位指出不足之处以及共同成长与学习。