在日常项目开发中活用设计模式,有时候可以做到事半功倍效果,提高代码设计的扩展性。在这篇文章中列举一些我在产线 spring 项目中,真实使用到的设计模式。由于 spring 框架的使用特殊性,我们必须对原生设计模式代码做一定的调整,做到活学活用。
业务场景:
我们有一个系统,event hub,主要用来接受各种平台发来的事件,进而告警和管控。每个系统发来的事件不同,对于不同系统,每个系统我们都需要写一个解析器,最原始最直接的代码是各种 if else if,但扩展性和封装性都很差。
要解决这种 if...else if 场景,可以考虑使用策略模式。
代码
首先定义一个接口EventHandler
public interface EventHandler {/*** 获取type 类型** @return*/String getType();/*** 对事件进行加工处理** @param event* @return*/JSONObject handleEvent(JSONObject event);}
实现不同系统的处理器,通过实现EventHandler接口,可以扩展更多系统,大大增强了系统的扩展性。这里举两个例子:
@Componentpublic class GrafanaEventHandler implements EventHandler {@Overridepublic String getType() {return "grafanaAlert";}@Overridepublic JSONObject handleEvent(JSONObject event) {//省略其他处理逻辑}}@Componentpublic class prometheusEventHandler implements EventHandler {@Overridepublic String getType() {return "prometheusAlert";}@Overridepublic JSONObject handleEvent(JSONObject event) {//省略其他处理逻辑}}
在系统中调用生成的各种处理器
@Componentpublic class EventHandlerFactory implements InitializingBean, ApplicationContextAware {private static final Map<String, EventHandler> HANDLER_MAP = new HashMap<>(16);private ApplicationContext appContext;/*** 根据提交类型获取对应的处理器** @param type 提交类型* @return 提交类型对应的处理器*/public EventHandler getHandler(String type) {return HANDLER_MAP.get(type);}@Overridepublic void afterPropertiesSet() {// 将 Spring 容器中所有的 EventHandler 注册到 HANDLER_MAPappContext.getBeansOfType(EventHandler.class).values().forEach(handler -> HANDLER_MAP.put(handler.getType(), handler));}@Overridepublic void setApplicationContext(@NonNull ApplicationContext applicationContext) {appContext = applicationContext;}}@Servicepublic class EventCollectService {@Autowiredprivate EventHandlerFactory eventHandlerFactory;public void receiveAlertMessage(JSONObject body, HttpServletRequest request) {String type = jsonObject.getString("type");EventHandler eventHandler = eventHandlerFactory.getHandler(type);if (eventHandler != null) {jsonObject = eventHandler.handleEvent(jsonObject);}//省略其他处理逻辑}}
通过以上例子,我们可以看到策略模式在spring应用中的实现,符合高内聚,可扩展的设计原则。新接入一个系统,只用实现EventHandler接口,其他地方不用修改,改动地方少意味着风险就小,待测方法就少。
业务场景:
我们发送促销email场景中,需要做很多数据检查,添加item详细数据,添加用户数据等。 它是在一条链上进行多次处理,该场景就可以使用责任链模式。
代码
public interface IHandler {/*** 执行逻辑,可以是过滤,可以是执行业务逻辑*/boolean execute(BusinessRequest request)throws HandlerException;}@Component@Order(100)public class FilterBehaviorEmailHandler implements IHandler {@Overridepublic boolean execute(BusinessRequest request) {//省略其他处理逻辑}}@Component@Order(200)public class FilterExcludeEmailHandler implements IHandler {@Overridepublic boolean execute(BusinessRequest request) {//省略其他处理逻辑}}
在业务中使用
@Servicepublic class EmailService {private final List<IHandler> handlers;public EmailService(List<IHandler> handlers) {this.handlers = handlers;}public void sendEmail() {//省略其他处理逻辑for (IHandler handler : handlers) {boolean flag = handler.execute(request);if (!flag) {break;}}}}
避免需要修改多个类的order顺序,建议使用三位数。新扩展一个handle更方便插入在任意位置。
业务场景:
我们的sns系统是一个消息统一发送平台,主要发送rest,email,teams,sms等消息。每次发送一个消息,都有一些公共操作:判重(避免重复发送),过滤消息,生成唯一id,记录log,重试,发送失败消息记录等。
这种具有多个公共处理逻辑,逻辑统一编排的业务完全可以使用模板方法来解决。
代码
public abstract class AbstractChannel{private boolean isOverdue(ChannelMessage channelMessage) {//省略}private boolean isFilter(ChannelMessage channelMessage) {//省略}private boolean isAllowedSend(ChannelMessage channelMessage) {//省略}private boolean log(ChannelMessage channelMessage) {//省略}abstract void handle(ChannelMessage channelMessage);public void send(ChannelMessage channelMessage){if(isOverdue(channelMessage)){return;}if(isFilter(channelMessage)){return;}if(isAllowedSend(channelMessage)){return;}send(channelMessage);log(channelMessage);}}@Componentpublic class EmailChannel extends AbstractChannel{@Overridevoid handle() {//省略}}@Componentpublic class SmsChannel extends AbstractChannel{@Overridevoid handle() {//省略}}@Componentpublic class TeamsChannel extends AbstractChannel{@Overridevoid handle() {//省略}}
在业务中使用可以直接使用相应的channel实例调用send,当然也可以结合策略模式更灵活的使用。
业务场景:
我们内部有这么个业务场景,需要根据活动用户的不同行为发送不同的促销邮件。不同促销邮件有大量的处理逻辑,如何封装?保持开闭原则,观察者模式是其中一种手段。
在spring应用中,我们没必要自己实现一套观察者模式,直接使用Spring的事件通知机制就可以了。
代码
// 定义一个事件public class BrowseEvent extends ApplicationEvent {private String message;public BrowseEvent(Object source, String message) {super(source);this.message = message;}public String getMessage() {return message;}}// 定义一个事件监听者@Componentpublic class BrowseEventListener implements ApplicationListener<BrowseEvent> {@Async@Overridepublic void onApplicationEvent(BrowseEvent event) {System.out.println("receiver " + event.getMessage());}}// 事件发布@Componentpublic class EventDemoPublish {@Autowiredprivate ApplicationEventPublisher applicationEventPublisher;public void publish(String message) {BrowseEvent demo = new BrowseEvent(this, message);applicationEventPublisher.publishEvent(demo);}}
默认情况下,Spring是同步执行Event的响应方法的。为了并行执行,我这里增加了@Async
,要想让异步生效还需要增加如下配置。
@Configuration@EnableAsyncpublic class AsyncTaskConfig {@Beanpublic SimpleAsyncTaskExecutor simpleAsyncTaskExecutor() {return new SimpleAsyncTaskExecutor();}}