设计模式-适配器模式

适配器模式定义

适配器模式是将一个类的接口变成客户端所期望的另一种接口,从而使原本因接口不匹配而导致无法在一起工作的两个类能够一起工作、

适配器模式使用场景

提供一个转换器,将当前系统存在的一个对象转化为客户端能够访问的接口对象,适用于以下业务场景:

  • 已经存在的类,它的方法和需求不匹配的情况
  • 适配器么送hi不是软件设计阶段考虑的设计模式,它是随着软件维护,由于不同产品,不同厂家造成功能类似而接口不同的情况下的解决方案,是一种亡羊补牢的措施

适配器模式组成部分

目标角色Target: 我们期望使用的/期望要兼容的接口
源角色Adaptee :已经存在于系统中,内容满足客户需求但是接口不匹配的接口实例
适配器Adapter: 将源角色Adaptee转换为目标角色Target的实例

适配器模式写法

适配器模式有三种形式:类适配器,对象适配器,接口适配器

  • 类适配器

类适配器的原理是通过继承来实现适配功能,让Adapter实现Target接口,并继承源角色Adaptee,这样Adapter同时具备Target和Adaptee的特性,在Target的接口实现方法中,就可以将两者进行转化

package com.gupaoedu.vip.pattern.adapter.poweradapter.classadapter;
/**
 * 目标接口:5V的标准接口,适配器需要实现该接口,完成适配逻辑
 */
public interface DC5 {
    int outputDC5V();
}
package com.gupaoedu.vip.pattern.adapter.poweradapter.classadapter;
// 源角色:需要转换的功能
public class AC220 {
    public int outPutAC220() {
        int output = 220;
        System.out.println("交流电压AC" + output);
        return output;
    }
}

package com.gupaoedu.vip.pattern.adapter.poweradapter.classadapter;
// 适配器,将22OV的交流电转换为手机充电器的5v直流电
public class PowerAdapter extends AC220 implements DC5 {
    @Override
    public int outputDC5V() {
        int adapterInput = super.outPutAC220();
        int adapterOutput = adapterInput/ 44;
        System.out.println("使用Adapter输入AC" + adapterInput + "V,输出DC" + adapterOutput  + "V");
        return adapterOutput ;
    }
}

测试类:

package com.gupaoedu.vip.pattern.adapter.poweradapter.classadapter;

public class PowerAdapterTest {
    public static void main(String[] args) {
        DC5 dc5 = new PowerAdapter();
        dc5.outputDC5V();
    }
}

UML如下:在这里插入图片描述

这里有个问题,如果改成PowerAdapter dc5 = new PowerAdapter(),那么 dc5也可以调用父类的outPutAC220() 方法,违背了最少知道原则,这是类适配器使用继承的一个缺点,可以通过适配器 对源对象进行组合的方式( 适配器持有源对象的引用)
来规避,也就是下面的对象适配器:
DC5接口不变

package com.gupaoedu.vip.pattern.adapter.poweradapter.objectadapter;
public interface DC5 {
    int outputDC5V();
}

源对象不变

package com.gupaoedu.vip.pattern.adapter.poweradapter.objectadapter;

public class AC220 {
    public int outPutAC220() {
        int output = 220;
        System.out.println("交流电压AC" + output);
        return output;
    }
}

适配器仍需实现DC5接口,但将源对象作为适配器的成员变量并通过构造参数传入

package com.gupaoedu.vip.pattern.adapter.poweradapter.objectadapter;

public class PowerAdapter implements DC5 {
    // 将源角色作为适配器的成员变量
    private AC220 ac220;

    public PowerAdapter(AC220 ac220) {
        this.ac220 = ac220;
    }

    @Override
    public int outputDC5V() {
        int adapterInput = ac220.outPutAC220();
        int adapterOutput = adapterInput / 44;  //适配逻辑
        System.out.println("使用Adapter输入AC:" + adapterInput + "V,输出DC:" + adapterOutput + "V");
        return adapterOutput;
    }
}

调用:这里DC5 dc5 = new PowerAdapter(new AC220()); 即使改为PowerAdapter dc5, 也不会再调用的到outPutAC220()了

package com.gupaoedu.vip.pattern.adapter.poweradapter.objectadapter;

public class PowerAdapterTest {
    public static void main(String[] args) {
        DC5 dc5 = new PowerAdapter(new AC220());
        dc5.outputDC5V();
     }
}

UML类图如下:
在这里插入图片描述
接口适配器:
类适配器和对象适配器重在将系统存在的一个角色转换为目标接口所需的内容,而接口适配器的使用场景是解决接口方法过多,如果直接实现接口,那么类会多出许多空实现的方法,类显得很臃肿,此时使用接口适配器就可以让我们只实现我们需要的接口方法,目标更明确
目标接口定义:

package com.gupaoedu.vip.pattern.adapter.poweradapter.interfaceadapter;

public interface DC {
    int output5V();
    int output12V();
    int output24V();
    int output36V();
}

需要转换的源角色:

package com.gupaoedu.vip.pattern.adapter.poweradapter.interfaceadapter;

public class AC220 {
    public int outPutAC220() {
        int output = 220;
        System.out.println("交流电压AC" + output);
        return output;
    }
}

适配器:

package com.gupaoedu.vip.pattern.adapter.poweradapter.interfaceadapter;


public class PowerAdapter implements DC {
    // 将源角色作为适配器的成员变量
    private AC220 ac220;

    public PowerAdapter(AC220 ac220) {
        this.ac220 = ac220;
    }

    @Override
    public int output5V() {
        int adapterInput = ac220.outPutAC220();
        int adapterOutput = adapterInput / 44;  //适配逻辑
        System.out.println("使用Adapter输入AC:" + adapterInput + "V,输出DC:" + adapterOutput + "V");
        return adapterOutput;
    }

    @Override
    public int output12V() {
        return 0;
    }

    @Override
    public int output24V() {
        return 0;
    }

    @Override
    public int output36V() {
        return 0;
    }
}

调用:

package com.gupaoedu.vip.pattern.adapter.poweradapter.interfaceadapter;

public class PowerAdapterTest {
    public static void main(String[] args) {
        DC dc5 = new PowerAdapter(new AC220());
       dc5.output5V();

    }
}

实际上,接口适配器虽然会避免类的膨胀,但是接口的功能变得不那么单一了,这也是它的缺点,实际使用时,根据情况决定使用哪一种类型的适配器模式;

适配器模式在源码中的应用

Spring中适配器模式用的很多,比如:

  • SpringAOP中的AdvisorAdapter接口
    AdvisorAdapter有三个实现类:AfterReturningAdviceAdapter,MethodBeforeAdviceAdapter和ThrowsAdviceAdapter
    AdvisorAdapter接口如下:
package org.springframework.aop.framework.adapter;

import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.Advisor;

/**
 * Interface allowing extension to the Spring AOP framework to allow handling of new Advisors and Advice types.
 */
public interface AdvisorAdapter {

	/**
	 * Does this adapter understand this advice object? Is it valid to invoke the {@code getInterceptors} method with an Advisor that contains this advice as an argument?
	 * @param advice an Advice such as a BeforeAdvice
	 * @return whether this adapter understands the given advice object
	 */
	boolean supportsAdvice(Advice advice);

	/**
	 * Return an AOP Alliance MethodInterceptor exposing the behavior of the given advice to an interception-based AOP framework.
	 */
	MethodInterceptor getInterceptor(Advisor advisor);

}
/*
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;

/**
 * Adapter to enable {@link org.springframework.aop.MethodBeforeAdvice} to be used in the Spring AOP framework.
 */
@SuppressWarnings("serial")
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {

	@Override
	public boolean supportsAdvice(Advice advice) {
	// 判断入参是不是前置通知
		return (advice instanceof MethodBeforeAdvice);
	}

	@Override
	public MethodInterceptor getInterceptor(Advisor advisor) {
		MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
		return new MethodBeforeAdviceInterceptor(advice);
	}
}

spring会根据不同的aop配置来确定使用对应的advice,跟策略模式不同的是,一个方法可以同时拥有多个Advice;

  • SpringMVC中的HandlerAdapter类
    HandlerAdapter及其子类的UML类图如下:
    在这里插入图片描述
    用的最多的是HttpRequestHandlerAdapter,首先判断入参的handler是不是HttpRequestHandlerAdapter ,如果是,才调用((HttpRequestHandler) handler).handleRequest(request, response)去处理请求
public class HttpRequestHandlerAdapter implements HandlerAdapter {

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

	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		((HttpRequestHandler) handler).handleRequest(request, response);
		return null;
	}

当一个请求进入springmvc的流程,会被DispatcherServlet类的doDispatch方法去处理

org.springframework.web.servlet.FrameworkServlet#doGet
==>org.springframework.web.servlet.FrameworkServlet#processRequest
==>org.springframework.web.servlet.FrameworkServlet#doService( 上面三个都是在父类中FrameworkServlet)
==> org.springframework.web.servlet.DispatcherServlet#doService (调用子类DispatcherServlet中的doService)
==> org.springframework.web.servlet.DispatcherServlet#doDispatch
具体适配调用的关键在于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 {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);

				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}

				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());

				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = "GET".equals(method);
				if (isGet || "HEAD".equals(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (logger.isDebugEnabled()) {
						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;
				}

				// Actually invoke the handler.
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());

				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}

				applyDefaultViewName(processedRequest, mv);
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

注意代码中 mappedHandler = getHandler(processedRequest),获取当前请求的handler adapter ; mappedHandler 保存了url和具体的controller的映射关系(spring容器初始化时保存的)
在这里插入图片描述
HandlerAdapter的实现有四个,如果是原生的,就用HttpRequestHandlerAdapter ,如果是注解的,就用 RequestMappingHandlerAdapter 去处理对应的请求在这里插入图片描述在这里插入图片描述

getHandlerAdapter()方法中,针对每一个具体的handler,又会有一个具体的HandlerAdapter,这里遍历所有的HandlerAdapters, 最终返回它能够支持的那一个adapter ,这个adapter,就是保存了某个URL对应的controller中的方法的具体信息的adapter.
在这里插入图片描述
获取了handlerAdapter之后,调用ha的方法实际处理请求: ha.handle(processedRequest, response, mappedHandler.getHandler());
在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值