在上一篇《策略模式-支持不同类型的消息队列》中,通过策略模式可以做到不修改代码的情况下进行消息队列的切换,这样就能够解决我们的需求,但上篇文章的接口设计带来了一些麻烦。根据上文我们了解,当在增加一个消息队列时,我们不仅仅需要增加一个策略类,还需要把外部系统对接的代码再实现一遍,代码明显出现了重复和无奈。例如:扩展ActiveMQ消息队列。
1、需要增加AbstractActiveMQProducerStrategy(这个是无可厚非的)。
2、需要增加ActiveMQProducerBySnmpGet。(这个就有问题了)
问题1:
其中ActiveMQProducerBySnmpGet是同一个外部系统的对接,里面的代码和KafkaProducerBySnmpGet、RabbitMQProducerBySnmpGet中实现的逻辑一模一样。
问题2:
现在需要在开发相同类型的外部系统对接,例如:SNMP有两种方式,一种是Get(poll数据),一种是Trap(push数据),这就出现了问题,因为queue.type已经指定了具体厂商生产者,而并不是消息队列的类型,问题还不小,如果继续下去,就会出现代码冗余,当然后续扩展消息队列的可能性不是很大,但是优化和重构必须是程序员的日常工作。
为什么会出现以上的问题呢?(当然这个和策略模式没有关系)
答案:而是在顶层接口设计的时候,违反了单一职责的原则。
我们的目的其实是想做到消息队列类型的切换,所以抽象出来的对象的职责应该是单一的,但是上篇文章中的接口出现了 厂商生产者 + 消息队列类型 两种职责,所以职责不明确,出现了以上的问题,我们再来看下ProducerStrategy接口。
/** * @author : zhangqing * @Description: 消息队列生产者策略,ActiveMQ,Kafka,RabbitMq等等 * @date : 2020年06月08日 16:33 */public interface ProducerStrategy<T> { /** * 获取来自不同厂商类型的数据,将数据进行处理,作为生产者数据的来源 */ public void produceData(); /** * 数据发送到队列 * @param data */ public void sendRecordToQueue(String data); /** * 获取消息队列生产者的客户端 * @return */ public T getProducerClient();}
怎么优化?将接口进行拆分为两个接口,每个接口的职责单一。
1、ProducerStrategy:发送数据到消息队列的生产者策略
/** * @author : zhangqing * @Description: 消息队列生产者策略,ActiveMQ,Kafka,RabbitMq等等 * @date : 2020年06月08日 16:33 */public interface ProducerStrategy<T> { /** * 数据发送到队列 * @param data * @param queue */ public void sendRecordToQueue(String queue,String data); /** * 获取消息队列生产者的客户端 * @return */ public T getProducerClient();}
2、OuterProtocol:外部系统,这里抽象成为外部协议,因为和外部系统对接,都是通过一种协议进行对接。
/** * @author : zhangqing * @Description: 外部系统接口,将外部系统抽象成为外部协议 * @date : 2020年06月10日 14:22 */public interface OuterProtocol { /** * 获取外部系统的数据并准备发送 */ public void getDataToSend();}
通过优化之后,原前的KafkaProducerBySnmpGet、RabbitMQProducerBySnmpGet没有了,只有一份DynamicMonitorSnmpGetProtocol,相同外部系统对接的代码只有一份,这样当增加一个消息队列的时候,只需要增加消息队列的策略,而在也不需要去关心外部系统对接的代码。完美。