1098 字
5 分钟
模板方法模式

介绍#

对原理类图的说明: AbstractClass 抽象类, 类中实现了模板方法(template),定义了算法的骨架,具体子类需要去实现 其它的抽象方法 ConcreteClass 具体类, 实现了抽象类中的抽象方法

也就是父类模板方法定义不变的流程,子类重写流程中的方法。

①、AbstractClass 抽象模板#

一、基本方法#

上面的 baseOperation() 或者 customOperation() 方法,也叫基本操作,是由子类实现的方法,并且在模板方法中被调用。

基本方法尽量设计为protected类型, 符合迪米特法则, 不需要暴露的属性或方法尽量不要设置为protected类型。 实现类若非必要, 尽量不要扩大父类中的访权限。

二、模板方法#

上面的 templateMethod() 方法,可以有一个或者几个,实现对基本方法的调度,完成固定的逻辑。

为了防止恶意操作,通常模板方法都加上 final 关键字,不允许覆写。

②、ConcreteClass 具体模板#

实现父类定义的一个或多个抽象方法,也就是父类定义的基本方法在子类中得以实现。

应用#

这个设计模式我们可以拿来做mq的发送和封装

我们在抽象模板中实现通用的sendMessage方法,在具体模板中实现具体的消息构建和封装发送逻辑。

定义消息发送事件基础扩充属性实体#

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public final class BaseSendExtendDTO {
/**
* 事件名称
*/
private String eventName;
/**
* 主题
*/
private String topic;
/**
* 标签
*/
private String tag;
/**
* 业务标识
*/
private String keys;
/**
* 发送消息超时时间
*/
private Long sentTimeout;
/**
* 具体延迟时间
*/
private Long delayTime;
}

我们这样定义抽象模板: AbstractCommonSendProduceTemplate

抽象模板#

/**
* RocketMQ 抽象公共发送消息组件
*/
@RequiredArgsConstructor
@Slf4j(topic = "CommonSendProduceTemplate")
public abstract class AbstractCommonSendProduceTemplate<T> {
private final RocketMQTemplate rocketMQTemplate;
/**
* 构建消息发送事件基础扩充属性实体
*
* @param messageSendEvent 消息发送事件
* @return 扩充属性实体
*/
protected abstract BaseSendExtendDTO buildBaseSendExtendParam(T messageSendEvent);
/**
* 构建消息基本参数,请求头、Keys...
*
* @param messageSendEvent 消息发送事件
* @param requestParam 扩充属性实体
* @return 消息基本参数
*/
protected abstract Message<?> buildMessage(T messageSendEvent, BaseSendExtendDTO requestParam);
/**
* 消息事件通用发送
*
* @param messageSendEvent 消息发送事件
* @return 消息发送返回结果
*/
public SendResult sendMessage(T messageSendEvent) {
BaseSendExtendDTO baseSendExtendDTO = buildBaseSendExtendParam(messageSendEvent);
SendResult sendResult;
try {
// 构建 Topic 目标落点 formats: `topicName:tags`
StringBuilder destinationBuilder = StrUtil.builder().append(baseSendExtendDTO.getTopic());
if (StrUtil.isNotBlank(baseSendExtendDTO.getTag())) {
destinationBuilder.append(":").append(baseSendExtendDTO.getTag());
}
// 延迟时间不为空,发送任意延迟消息,否则发送普通消息
if (baseSendExtendDTO.getDelayTime() != null) {
sendResult = rocketMQTemplate.syncSendDeliverTimeMills(
destinationBuilder.toString(),
buildMessage(messageSendEvent, baseSendExtendDTO),
baseSendExtendDTO.getDelayTime()
);
} else {
sendResult = rocketMQTemplate.syncSend(
destinationBuilder.toString(),
buildMessage(messageSendEvent, baseSendExtendDTO),
baseSendExtendDTO.getSentTimeout()
);
}
log.info("[生产者] {} - 发送结果:{},消息ID:{},消息Keys:{}", baseSendExtendDTO.getEventName(), sendResult.getSendStatus(), sendResult.getMsgId(), baseSendExtendDTO.getKeys());
} catch (Throwable ex) {
log.error("[生产者] {} - 消息发送失败,消息体:{}", baseSendExtendDTO.getEventName(), JSON.toJSONString(messageSendEvent), ex);
throw ex;
}
return sendResult;
}
}

具体模板#

这里举个例子,我们做一个短信通知的,定义MessageNotifyEvent

@Slf4j
@Component
public class SmsNotificationProducer extends AbstractCommonSendProduceTemplate<MessageNotifyEvent> {
private final ConfigurableEnvironment environment;
public SmsNotificationProducer(@Autowired RocketMQTemplate rocketMQTemplate, @Autowired ConfigurableEnvironment environment) {
super(rocketMQTemplate);
this.environment = environment;
}
@Override
protected BaseSendExtendDTO buildBaseSendExtendParam(MessageNotifyEvent messageSendEvent) {
return BaseSendExtendDTO.builder()
.eventName("短信通知发送")
.keys(String.valueOf(messageSendEvent.getNotificationId()))
.topic(environment.resolvePlaceholders(MerchantAdminRocketMQConstant.SMS_NOTIFICATION_TOPIC_KEY))
.sentTimeout(3000L)
.build();
}
@Override
protected Message<?> buildMessage(MessageNotifyEvent messageSendEvent, BaseSendExtendDTO requestParam) {
String keys = StrUtil.isEmpty(requestParam.getKeys()) ? UUID.randomUUID().toString() : requestParam.getKeys();
return MessageBuilder
.withPayload(new MessageWrapper(keys, messageSendEvent))
.setHeader(MessageConst.PROPERTY_KEYS, keys)
.setHeader(MessageConst.PROPERTY_TAGS, requestParam.getTag())
.build();
}
}

补充说明#

为了使上述示例能够完整运行,还需要定义以下类:

  1. MessageNotifyEvent - 短信通知事件类:
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class MessageNotifyEvent {
private Long notificationId;
private String phoneNumber;
private String messageContent;
private String sender;
}
  1. MessageWrapper - 消息包装类:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MessageWrapper {
private String key;
private Object payload;
public MessageWrapper(String key, Object payload) {
this.key = key;
this.payload = payload;
}
}
  1. SmsNotificationConsumer - 短信通知消费者类:
@Slf4j
@Component
@RocketMQMessageListener(
topic = MerchantAdminRocketMQConstant.SMS_NOTIFICATION_TOPIC_KEY,
consumerGroup = MerchantAdminRocketMQConstant.SMS_NOTIFICATION_CONSUMER_GROUP
)
public class SmsNotificationConsumer implements RocketMQListener<MessageWrapper> {
@Override
public void onMessage(MessageWrapper messageWrapper) {
MessageNotifyEvent event = (MessageNotifyEvent) messageWrapper.getPayload();
log.info("[短信消费者] 收到短信通知消息 - ID: {}, 手机号: {}, 内容: {}, 发送者: {}",
event.getNotificationId(),
event.getPhoneNumber(),
event.getMessageContent(),
event.getSender());
// 模拟发送短信
System.out.println("[短信服务] 向 " + event.getPhoneNumber() + " 发送短信: " + event.getMessageContent());
}
}

通过以上完整的代码示例,我们可以看到模板方法模式在MQ消息发送场景中的应用:

  • AbstractCommonSendProduceTemplate 定义了消息发送的通用流程(模板方法)
  • SmsNotificationProducer 实现了具体的业务逻辑(构建参数和消息)
  • SmsNotificationConsumer 监听消息并处理
  • 这样既保证了消息发送流程的一致性,又允许不同业务场景自定义具体实现