1.模板设计模式:
模板设计模式是一种常见的设计模式,主要作用是对 具体操作的 共有代码块进行提取,提升代码复用性。
那么说道代码复用性,首先想到的是抽象类而不是接口。
因为抽象类的本质就是为了代码复用,抽象类既可以包含抽象方法也可以包含具体方法。
在模板设计模式中,我们将 需要将原本共有的具体操作抽离并封装在抽象类的具体方法中。让抽象类的具体操作来实现需要被复用的逻辑。而其余的抽象方法是不同业务的 个性,各个业务可以按照自己的方式进行重写。这部分特性在模板设计模式中是为具体方法服务的。
2.开发优惠券推送功能
优惠券推送功能是根据excel导出方式来进行的。目前我们有一张优惠券,画像平台根据用户特性选取出符合这张优惠券的用户,从而导出一个excel表格,表格中装入的是符合发券特征的用户。我们的优惠券推送功能就是根据读取excel表格中的用户信息,将这张优惠券统一推送给目标用户。
对于优惠券发放,我们考虑到了定时发放和立即发放两种情况。由于定时发放使用传统的定时任务需要建立线程定时去扫描全表,造成数据库的较大开销,所以考虑使用RocketMQ来进行定时任务。
RocketMQ的延时消息任务,需要发送地址、消息以及发送时间三个参数。
public SendResult syncSendDelayTimeMills(String destination, Message<?> message, long delayTime) {return this.syncSend(destination, message, (long)this.producer.getSendMsgTimeout(), delayTime, DelayMode.DELAY_MILLISECONDS);
}
这是对应的rocketMQ的定时消息发送接口,需要destination,message,delayTime
2.1发送地址
对于不同的业务,建立不同的group,消费者会根据group从消息队列中接收自己想要的信息。
2.2消息
需要注意的是Message<?>是指上是Spring中的包import org.springframework.messaging.Message;
Message通过MessageBuilder进行构建,这部分内容就是消息实体。
2.3发送时间
delayTime就是指延迟发送的时间。
3.如何抽离出模板设计模式
由于我们的业务不仅有延时发送任务,还有立即发送任务,所以会为这两种任务设计消息发送类AbstractCommonSendProduceTemplate,很显然,这是共有的抽象类。
然后为不同的业务创建不同的实现类,继承AbstractCommonSendProduceTemplate。
3.1如何判断是立即发送还是延时发送?
我们只需要设计一个属性,根据属性的值来判断即可。这可以解决立即发送和延时发送的矛盾。在业务层无需再进行判断。
3.2具体如何完整实现模板设计模式?
在上面我们讲解了类的抽离,我们现在需要找立即发送与延时发送的区别,抽离出共性与特性。
共性很显然就是都需要使用rocketMQ进行消息发送。
1.特性:对于立即发送而言,需要的api不是syncSendDelayTimeMills而是syncSend,
public SendResult syncSend(String destination, Message<?> message, long timeout) {return this.syncSend(destination, message, timeout, 0);
}
timeout是超时时间。这是第一个特性。
2.处理的业务模块不同,在项目中,推送优惠券模块的推送功能可以是定时推送也可以是立即推送,包含这两种方式。
而对于优惠券创建模块的定时销毁过期优惠券功能目前只需要定时任务方式。
所以对于不同的业务,我们需要抽离出不同的事件,因为不同的业务需要的参数不同,这里我们的项目定义了两个事件类:CouponTaskExecuteEvent、CouponTemplateDelayEvent
基于上述的共性与特性,具体如何实现?
从顶层考虑,我们的目的是实现发送消息代码的复用,所以我们抽离代码的时候一般而言是先抽取主要逻辑,对应抽象类中的具体方法。
当具体方法因不同业务模块需要不同参数的时候我们再使用抽象方法对”个性“进行实现即可。也就是上述内容所说的抽象方法为具体方法服务的思想。
因为定时和立即发送api不同,所以我们需要一个dto类来传递一个属性来验证是定时发送还是立即发送。
所以现在我们需要创建一个共有的DTO类来辅助具体任务的开发。DTO应该包含多个业务的主要内容,所以对于这种模板设计模式而言,尽量使业务功能相近,不然在DTO设计时不好设计。
public class BaseSendExtendDTO {/*** 事件名称*/private String eventName;/*** 主题*/private String topic;/*** 标签*/private String tag;/*** 业务标识*/private String keys;/*** 发送消息超时时间*/private Long sentTimeout;/*** 具体延迟时间*/private Long delayTime;
}
事件名称用于分辨不同功能,主题用于不同消息类型的监听,如果是立即发送,则delayTime为null,用于判断信息类型。
需要注意的是,该DTO类只是用于辅助消息发送,和真正的业务无关。比如说用于判别目前的任务是那个业务模块,执行业务模块中的哪个任务,任务的信息发送是什么模式等等。
比如在:
public SendResult syncSendDelayTimeMills(String destination, Message<?> message, long delayTime) {
return this.syncSend(
destination, message,
(long)this.producer.getSendMsgTimeout(),
delayTime, DelayMode.DELAY_MILLISECONDS);
}
这个 发送api中,DTO类只控制destination与delayTime,而message是由事件决定的。
4.总结
通过上述DTO可以使得不同功能模块找到自己的实现方式(比如立即发送和延时发送就是通过判别DTO中delayTime是否为null,再比如通过Topic使得不同消费者只监听自己的内容)。
通过两个事件类对两个功能模块所需属性做了区分,实质上最终传递的内容就是对应的事件。