适配器模式- 2020-12-05 17:16- 设计模式: 适配器模式,设计模式
适配器模式(Adapter Pattern)是指将一个类的接口转换成客户期望的另一个接口,使 原本的接口不兼容的类可以一起工作,属于结构型设计模式。
适用场景
1、已经存在的类,它的方法和需求不匹配(方法结果相同或相似)的情况。
2、适配器模式不是软件设计阶段考虑的设计模式,是随着软件维护,由于不同产品、不 同厂家造成功能类似而接口不相同情况下的解决方案。
优点
1、能提高类的透明性和复用,现有的类复用但不需要改变。
2、目标类和适配器类解耦,提高程序的扩展性。
3、在很多业务场景中符合开闭原则。
缺点
1、适配器编写过程需要全面考虑,可能会增加系统的复杂性。
2、增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。
demo
模拟电源适配
在中国民用电都是 220V 交流电,但我们手机使用的锂电池使用的 5V 直流电。因此,我 们给手机充电时就需要使用电源适配器来进行转换。
/**
* 220V电压
*
* @author Bai
* @date 2020/12/5 16:12
*/
public class AC220Service {
/**
* 输出220V电压
*
* @return
*/
public int out220V() {
System.out.println("输出220V电流");
return 220;
}
}
/**
* 5V电流
*
* @author Bai
* @date 2020/12/5 16:13
*/
public interface DC5VService {
/**
* 输出5V
*
* @return
*/
int out5V();
}
/**
* 电流适配器:将220V转换为5V
*
* @author Bai
* @date 2020/12/5 16:14
*/
public class PowerAdapter implements DC5VService {
@Override
public int out5V() {
AC220Service ac220Service = new AC220Service();
//输出220V
int ac220 = ac220Service.out220V();
//转换5V
int dc5v = ac220 / 44;
System.out.println("输出5V:" + dc5v);
//输出5V
return dc5v;
}
}
@Test
public void demo() {
DC5VService dc5VService = new PowerAdapter();
dc5VService.out5V();
}
输出220V电流
输出5V:5
模拟用户登录
系统原有登录方式为用户名&密码,现在越来越多的登录方式也出现了,例如微信、QQ、手机号等,所以现有的登录方式已经不能满足业务需求了,在不改动原有逻辑的情况下,增加微信、QQ、手机号登录方式。采用 适配器+简单策略模式改造,策略模式其实还可以优化为每种登录方式作为一个Adapter。
/**
* 模拟用户根据用户名登录
*
* @author Bai
* @date 2020/12/5 16:23
*/
public class LogService {
public boolean loginByName() {
System.out.println("根据用户名登录");
return true;
}
}
/**
* 登录适配器:遵循开闭原则,不改动原来登录的逻辑,新增微信、QQ、手机号登录方式
* 适配模式+策略模式(简单的策略模式,复杂点可将每种登录方式封装为单独的Adapter)
*
* @author Bai
* @date 2020/12/5 16:24
*/
public class LoginAdapter {
/**
* 适配器+简单的策略模式
*
* @param loginType
* @return
*/
public boolean login(String loginType) {
if ("weixin".equals(loginType)) {
return weixinLogin();
} else if ("QQ".equals(loginType)) {
return qqLogin();
} else if ("mobile".equals(loginType)) {
return mobileLogin();
} else if ("loginName".equals(loginType)) {
LogService logService = new LogService();
return logService.loginByName();
}
return false;
}
/**
* 微信登录
*
* @return
*/
public boolean weixinLogin() {
System.out.println("微信登录");
return true;
}
/**
* QQ登录
*
* @return
*/
public boolean qqLogin() {
System.out.println("QQ登录");
return true;
}
/**
* 手机号登录
*
* @return
*/
public boolean mobileLogin() {
System.out.println("手机号登录");
return true;
}
}
@Test
public void demo() {
LoginAdapter loginAdapter = new LoginAdapter();
loginAdapter.login("weixin");
loginAdapter.login("QQ");
loginAdapter.login("loginName");
}
适配器模式在源码中的体现
SpringAOP 中的 AdvisorAdapter
SpringAOP 中的 AdvisorAdapter 类, 它有三个实现类 MethodBeforeAdviceAdapter、AfterReturningAdviceAdapter 和
ThrowsAdviceAdapter,先来看顶层接口 AdvisorAdapter 的源代码:
package org.springframework.aop.framework.adapter;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.Advisor;
public interface AdvisorAdapter {
boolean supportsAdvice(Advice var1);
MethodInterceptor getInterceptor(Advisor var1);
}
再看 MethodBeforeAdviceAdapter 类:
package org.springframework.aop.framework.adapter;
import java.io.Serializable;
import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.Advisor;
import org.springframework.aop.MethodBeforeAdvice;
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
MethodBeforeAdviceAdapter() { }
public boolean supportsAdvice(Advice advice) {
return advice instanceof MethodBeforeAdvice;
}
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice)advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
Spring 会根据不同的 AOP 配置来确定使用对 应的 Advice,跟策略模式不同的一个方法可以同时拥有多个 Advice
SpringMVC 中的 HandlerAdapter
其适配调用的关键代码还是在 DispatcherServlet 的 doDispatch()方法中,下面我们还是来看源码:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
mappedHandler = this.getHandler(processedRequest);
if(mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if(isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if(this.logger.isDebugEnabled()) {
this.logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified)
}
if((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
if(!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if(asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if(asyncManager.isConcurrentHandlingStarted()) {
if(mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if(multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
在 doDispatch()方法中调用了 getHandlerAdapter()方法,来看代码:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if(this.handlerAdapters != null) {
Iterator var2 = this.handlerAdapters.iterator();
while(var2.hasNext()) {
HandlerAdapter ha = (HandlerAdapter)var2.next();
if(this.logger.isTraceEnabled()) {
this.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");
}
在 getHandlerAdapter()方法中循环调用了 supports()方法判断是否兼容,循环迭代集 合中的 Adapter 又是在初始化时早已赋值。
参考资料
咕泡VIP架构师课程
**