设计模式---适配器模式(springMvc中HandlerAdapter 的误区)

适配器模式做的就是,有一个接口需要实现,但是我们现成的对象都不满足,需要加一层适配器来进行适配。适配器模式总体来说分三种:默认适配器模式、对象适配器模式、类适配器模式。那我们生活中很常见的一个例子来讲,比如你水龙头接口是4cm,而房间室内接入管道是10cm此时我们没法直接对接,就需要一个中间件来联通水龙头与管道这个过程就叫适配,水暖上名字称为大小头咳咳跑远了。
在这里插入图片描述

默认适配器模式:

首先,我们先看看最简单的适配器模式默认适配器模式(Default Adapter)是怎么样的。我们用 Appache commons-io 包中的 FileAlteration Listener 做例子,此接口定义了很多的方法,用于对文件或文件夹进行监控,一旦发生了对应的操作,就会触发相应的方法。

public interface FileAlterationListener {
    void onStart(final FileAlterationObserver observer);
    void onDirectoryCreate(final File directory);
    void onDirectoryChange(final File directory);
    void onDirectoryDelete(final File directory);
    void onFileCreate(final File file);
    void onFileChange(final File file);
    void onFileDelete(final File file);
    void onStop(final FileAlterationObserver observer);
}

此接口的一大问题是抽象方法太多了,如果我们要用这个接口,意味着我们要实现每一个抽象方法,如果我们只是想要监控文件夹中的文件创建和文件删除事件,可是我们还是不得不实现所有的方法,很明显,这不是我们想要的。所以,我们需要下面的一个适配器,它用于实现上面的接口,但是所有的方法都是空方法。这样,我们就可以转而定义自己的类来继承下面这个类即可。

public class FileAlterationListenerAdaptor implements FileAlterationListener {

    public void onStart(final FileAlterationObserver observer) {
    }

    public void onDirectoryCreate(final File directory) {
    }

    public void onDirectoryChange(final File directory) {
    }

    public void onDirectoryDelete(final File directory) {
    }

    public void onFileCreate(final File file) {
    }

    public void onFileChange(final File file) {
    }

    public void onFileDelete(final File file) {
    }

    public void onStop(final FileAlterationObserver observer) {
    }
}

比如我们可以定义以下类,我们仅仅需要实现我们想实现的方法就可以了:

public class FileMonitor extends FileAlterationListenerAdaptor {
    public void onFileCreate(final File file) {
        // 文件创建
        doSomething();
    }

    public void onFileDelete(final File file) {
        // 文件删除
        doSomething();
    }
}

当然,上面只是适配器模式的其中一种,也是最简单的一种平时工作中基本不会使用这种形式。

对象适配器模式:

public interface Duck {
    public void quack(); // 鸭的呱呱叫
    public void fly(); // 飞
}

public interface Cock {
    public void gobble(); // 鸡的咕咕叫
    public void fly(); // 飞
}

public class WildCock implements Cock {
    public void gobble() {
        System.out.println("咕咕叫");
    }
    public void fly() {
        System.out.println("鸡也会飞哦");
    }
}

鸭接口有 fly() 和 quack() 两个方法,鸡 Cock 如果要冒充鸭,fly() 方法是现成的,但是鸡不会鸭的呱呱叫,没有 quack() 方法。这个时候就需要适配了:

// 毫无疑问,首先,这个适配器肯定需要 implements Duck,这样才能当做鸭来用
public class CockAdapter implements Duck {

    Cock cock;
    // 构造方法中需要一个鸡的实例,此类就是将这只鸡适配成鸭来用
      public CockAdapter(Cock cock) {
        this.cock = cock;
    }

    // 实现鸭的呱呱叫方法
    @Override
      public void quack() {
        // 内部其实是一只鸡的咕咕叫
        cock.gobble();
    }

      @Override
      public void fly() {
        cock.fly();
    }
}

在这里插入图片描述

类适配器模式:

在这里插入图片描述

HanlderAdapter真的采用了适配器模式么?

spring中大量使用到了适配器这种设计模式,就拿最经典的springMvc中的九大组件之一的 HanlderAdapter,从名字看就明白了这里十有八九用到了适配器模式但是叫Adapter就真的采用了适配器模式么??????看到百分之九十九的文章都打着适配器模式的幌子,但是本人并不认同:

  • springmvc通过HandlerMapping获取到可以处理的handler,这些handler的类型各不相同,对请求的预处理,参数获取都不相同,最简单的做法是根据不同的handler类型,做一个分支处理,不同的handler编写不同的代码。这样的问题是很明显的,分支判断复杂,代码庞大,不符合单一职责原则。如果要增加一种handler类型,需要修改代码增加分支处理,违反了开闭原DispatcherServelt与多个handler发生了交互,违反迪米特法则。而使用适配器模式,就可以很好的解决这个问题。
  • 不直接对handler进行处理,而是将handler交给适配器HandlerAdapter去处理,这样DispatcherServlet交互的类就只剩下一个接口,Handl erAdapter,符合迪米特法则,尽可能少的与其他类发生交互,将handler交给HandlerAdapter处理后,不同类型的handler被对应类型的Han dlerAdapter处理,每个HandlerAdapter都只完成单一的handler处理,符合单一职责原则。
public interface HandlerAdapter {

	/**
	 * 判断是否可以使用某个handler
	 */
	boolean supports(Object handler);

	/**
	 * 具体使用handler干活
	 */
	@Nullable
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

	/**
	 * 获取资源最后一次修改的时间戳
	 */
	long getLastModified(HttpServletRequest request, Object handler);

}

adapter的作用就是当请求过来时,使用实现了controller接口的的处理器来干活。干活的方法是直接调用处理器的handleRequest ( HttpSer vletRequest request, HttpServletResponse response)方法。这里我们可以看出对应不同的请求supports和getLastModified两个方法都是一样的。但是handle处理方法却要因请求的方式不同而异。HttpRequestHandlerAdapter、SimpleServletHandlerAdapter和Simple Controller Adapter分别适配了HttpRequestHandler、Servlet和Controller类型的Handler方法非常简单都是调用Handler里的固定方法。选择哪个Handl erAdapter的过程在getHandlerAdapter方法中,它的逻辑是遍历所有的Adapter,然后检查哪个可以处理当前的handler,找到第一个可以处理Handler的Adapter后停止查找并将其返回。HttpRequestHandlerAdapter

DispatcherServlet中的doDispatch方法:

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
          。。。。。。。。。。。。。。。
			try {
				// 检查请求有没有携带二进制文件,如何有则需要在http请求头中进行申明Multipart-form-data
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request. 确定当前请求的处理程序
				// 1.获得当前请求的handler,在通常情况下其实就是controller对象但不完全一定是
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// getHandler方法主要会调用已经注册好了的handlerMapping中的getHandler方法
				// Determine handler adapter for the current request.
				// 2.根据找到的handler找到对应的handlerAdapter
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
				。。。。。。。。。。。。。。。。。。。。。。。。。。。。
}

这样假设如果我们增加一个HardController,就要在代码中加入一行 if(mappedHandler.getHandler() instanceof HardController)
这种形式就使得程序难以维护,也违反了设计模式中的开闭原则 – 对扩展开放,对修改关闭。 因此Spring定义了一个适配接口,使得每一种Controller有一种对应的适配器实现类, 让适配器代替controller执行相应的方法。这样在扩展Controller 时,只需要增加一个适配器类就完成了SpringMVC的扩展了。

public class SimpleControllerHandlerAdapter implements HandlerAdapter {

	@Override
	public boolean supports(Object handler) {
		return (handler instanceof Controller);
	}

	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		/**
		 * {@link AbstractController#handleRequest(HttpServletRequest, HttpServletResponse)}
		 */
		return ((Controller) handler).handleRequest(request, response);
	}

	@Override
	public long getLastModified(HttpServletRequest request, Object handler) {
		if (handler instanceof LastModified) {
			return ((LastModified) handler).getLastModified(request);
		}
		return -1L;
	}

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

可以明显的看到在获取适配器时是通过不断遍历,调用supports方法使用instanceof 关键字来进行判断的。如果符合则返回对应适配器类。

SimpleServletHandlerAdapter:
@Override
	public boolean supports(Object handler) {
		return (handler instanceof Servlet);
	}
SimpleControllerHandlerAdapter:
@Override
	public boolean supports(Object handler) {
		return (handler instanceof Controller);
	}
HttpRequestHandlerAdapter:
@Override
	public boolean supports(Object handler) {
		return (handler instanceof HttpRequestHandler);
	}
	

到这里我认为,HandlerAdapter 本身和适配器设计模式并不沾边,不过设计模式本身也只是一种思想并不固定。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值