首页 > 其他 > 详细

猜猜这段代码用到了什么设计模式?

时间:2021-04-29 18:03:37      阅读:21      评论:0      收藏:0      [点我收藏+]

下面是我在项目中编写的一段代码,代码略做了改动。

	private List<RabbitHandler> rabbitHandlers = new ArrayList<>(8);

	Handler getHandler(Object message){
		for (RabbitHandler handler : this.rabbitHandlers) {
			if (handler.supports(message)) {
				return handler;
			}
		}
	}
	
	handler.handle(message);

上面这段代码很简单,很容易看懂,但我还是强行解释一波。代码的意思大致是这样的:我们项目中用到了RabbitMQ来做消息处理,消息可以有不同的topic。由于消费者较多,所以我用了一个ArrayList来保存所有消费者,消费者收到消息后就要遍历处理器列表,检查哪个处理器能够处理该消息,然后就调用该处理器去处理。
现在,我的问题是上面代码用到了什么设计模式?

实际上上面这种形式的代码见得简直太多,不就是遍历一个列表吗?确实是这样,我当初也这么认为,但总感觉这里应该是用到了某种设计模式,而且还感觉在哪里看过。突然我灵光一闪,想到了SpringMVC中的一段代码(之前学习SpringMVC的工作原理时看过一点源码),我把它贴出来,如下所示。

void doDispatch(HttpServletRequest request, HttpServletResponse response)

	HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
	……
	mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
	
}

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	for (HandlerAdapter ha : this.handlerAdapters) {
		if (logger.isTraceEnabled()) {
			logger.trace("Testing handler adapter [" + ha + "]");
		}
		if (ha.supports(handler)) {
			return ha;
		}
	}
	throw new ServletException("No adapter for handler [" + handler +
			"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

what‘s up(我擦),几乎一样啊,SpringMVC都已经给出答案了——适配器模式。还真让人哑然而笑,没有刻意去用设计模式却无意用上了,无形装逼最致命。

适配器模式

下面解释一下SpringMVC使用适配器模式的原因。
SpringMVC提供了多种实现Controller的方式。

//方式一
class MyController implements Controller

//方式二
@Controller
class MyController{}

//方式三
class MyController implements HttpRequestHandler{}

//其它方式……

而且,处理请求的处理器(handler)不一定就是Controller,可以是下面几种:

  • Controller
  • HttpRequestHandler
  • Servlet
  • @RequestMapping

每一种处理器的处理逻辑都是不同的,必然会有下面这样的代码

if(A){
	//处理方式
}else if(B){
	//处理方式
}else if(C){
	//处理方式
}
*****

而SpringMVC为了将不同处理器的处理流程统一起来,于是为每种处理器提供了一个适配器,这就叫做适配器模式。正是由于大量使用设计模式,Spring源码看起来才如此优雅美观。
技术分享图片

查找对应处理器的过程就是一个适配的过程。不同的处理流程统一成了一致的形式,代码如下所示。

	HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
	……
	mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

经过上面的分析,似乎前面的问题已经有了答案了。且慢!我们再来看一下另外一种设计模式。

责任链模式

在上面思考的过程中,我又想到了责任链模式。
责模式模式是将不同的处理器组成处理器链(责任链),当请求(消息)到达后,只需要遍历一遍责任链,遍历过程中如果有处理器能处理就处理,如果不能处理就检查下一个处理器。

实现标准的责任链模式时,每个处理器需要持有下一个处理器。但也可以有变种形式,就是将所有处理器串在一起组成链条。而上面我们的代码从形式上来看正好如此。照这样看,上面代码岂不也使用了责任链模式?

理解设计模式

对于到底使用的是适配器模式,还是责任链模式,我想了很久才终于想通。实际上,一段代码使用设计模式是有使用意图的,一定是为了解决某类问题才引入的。我前面仅通过代码层面来判断是否用到了某种设计模式,显然本末倒置了。
另外,总共23种设计模式,其中本来就有很多设计模式是很相似的,如果仅从代码层面去理解,很容易就被绕进去。

现在,回过头再来看前面的代码,尝试从使用意图来重新分析一下。这里我先列出责任链模式和适配器模式的意图。
责任链模式的意图:

使多个对象都有机会处理请求,从而避免请求的发送者和接收者之间的耦合关系。将这
些对象连成一条链,并沿着这条链传递该请求,直到有一个对象处理它为止。

适配器模式的意图:

将一个类的接口转换成客户希望的另外一个接口。 Adapter模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

前面代码中,我将消息处理器RabbitHandler存储到列表中,本意是想将所有处理器统一放在一块,甭管来的是什么形式的消息,只管去列表里面找出能处理它的处理器就行了,实际上更多的是想将消息和处理解耦。所以说代码用到的责任链模式。另外,这里的MQ消息只是我在业务层面给他们一个topic分类,但实际都是同类型,可以统一用Message来描述。

那什么时候才能说代码用的是适配器模式呢?重新回顾下上面SpringMVC中的代码就清楚了——当我想要强调MQ消息的种类的多样性、异构性时。说的更直白一些,就是MQ消息并不能抽象成同一类别,比如,一个系统中可以有mq消息,短信消息,邮件消息,这些并不能简单的划分为同一类别,也就是不能统一用Message描述,而只能用Object来描述。就像SpringMVC中的Handler可以是Controller, Servlet, HttpRequestHandler, @RequestMapping一样。
对于这样的情况,我们可以为每种消息提供一个适配器,代码实现上可以将这些适配器组成链,而查找对应适配器的过程就是一个适配的过程。这也就跟前面SpringMVC的getHandlerAdaptor()的逻辑对应上了,自然可以称之为适配器模式。只是这里的适配器叫做Adaptor,而责任链模式中的处理器叫做Handler,代码形式都是遍历for循环的形式。建议适配器模式还是使用Adator名称更好,一目了然。

总结

所以,最后总结一下:
1.不要简单地仅从代码层面去判断某段代码用到了什么设计模式,而要从代码的实现意图去理解需要引入什么设计模式。
2.适配器模式中,由于原目标不能简单的划分为同一类别(出现不适配),而需要分开处理,可以引入适配器将处理流程统一。
3.责任链模式,允许将处理器组成责任链。请求到来后,在链中依次检测每个处理器。每个处理器只负责处理一种请求(消息)。如果处理器能处理则负责处理,不能处理则交给下一个。责任链模式实现请求和处理的解耦。

本文涉及到了适配器模式、责任链模式、SpringMVC的工作原理。

猜猜这段代码用到了什么设计模式?

原文:https://www.cnblogs.com/rouqinglangzi/p/14710731.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!