设计模式篇(Java):建造者模式
八、适配器模式
8.1 适配器模式基本介绍
生活中的适配器例子
- 比如生活中的插座,在不同国家插座有着不同的规格,如果我们从一个国家去另外一个国家需要使用插座时就需要一个中间转换器把两种不同规则的插座适配一下。
- 生活中电脑充电器,手机充电器,手机电脑可能支持20v充电,但是生活用电时220v,所以产家加载充电器上做了适配器,把插座过来的220v适配成手机能适应的20v
基本介绍
- 适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)。
- 适配器模式属于结构型模式。
- 主要分为三类:类适配器模式、对象适配器模式、接口适配器模式。
工作原理
- 适配器模式:将一个类的接口转换成另一种接口。让原本接口不兼容的类可以兼容。
- 从用户的角度看不到被适配者,是解耦的。
- 用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法。
- 用户收到反馈结果,感觉只是和目标接口交互。用户感受不到适配的过程。
8.2 类适配器模式
生活示例:充电器。充电器本身相当于Adapter,220V交流电相当于src(即被适配者),我们的目dst(即目标)是20V直流电。
代码示例
被适配的类(220V)
/**
* @author cVzhanshi
* @create 2023-06-25 16:33
*/
@Slf4j
public class Voltage220V {
public int output220V() {
int src = 220;
log.info("电压 = {}V", src);
return src;
}
}
适配接口
/**
* @author cVzhanshi
* @create 2023-06-25 16:35
*/
public interface IVoltage5V {
int output5V();
}
适配类
/**
* @author cVzhanshi
* @create 2023-06-25 16:36
*/
public class VoltageAdapter extends Voltage220V implements IVoltage5V{
@Override
public int output5V() {
// 获得220v电压
int src = output220V();
// 转化成手机能用的5v
int dst = src / 44;
return dst;
}
}
手机类以及客户端
/**
* @author cVzhanshi
* @create 2023-06-25 16:38
*/
@Slf4j
public class Phone {
// 充电
public void init (IVoltage5V iVoltage5V) {
if (iVoltage5V.output5V() == 5) {
log.info("电压为5v,可以充电");
} else {
log.info("电压大于5v,不能充电");
}
}
}
/**
* @author cVzhanshi
* @create 2023-06-25 16:40
*/
public class Client {
public static void main(String[] args) {
Phone phone = new Phone();
phone.init(new VoltageAdapter());
}
}
这样就是通过适配器解决了电压不适配的问题
类适配器模式注意事项和细节
- Java是单继承机制,所以类适配器需要继承被适配类这一点算是一个缺点,因为这要求dst必须是接口,有一定局限性。
- src类的方法在Adapter中都会暴露出来,也增加了使用的成本。
- 由于其继承了src类,所以它可以根据需求重写src类的方法,使得Adapter的灵活性增强了。
8.3 对象适配器模式
- 与类适配器唯一不同的是,对象适配器模式是持有src类,而不是继承src类。
- 使用了关联或者聚合的关系代替了继承。
那么类图就变成了
代码主要是适配器代码的区别:
适配器
/**
* @author cVzhanshi
* @create 2023-06-25 16:36
*/
public class VoltageAdapter extends Voltage220V implements IVoltage5V{
private Voltage220V voltage220V;
public VoltageAdapter (Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}
@Override
public int output5V() {
// 获得220v电压
int src = voltage220V.output220V();
// 转化成手机能用的5v
int dst = src / 44;
return dst;
}
}
对象适配器的注意事项和细节
-
组合代替了继承,解决了类适配器需要继承src的局限性,也不需要要求dst必须是接口。
-
使用成本低,更灵活。
8.4 接口适配器模式
- 接口适配器模式也可以称为缺省适配器模式。
- 核心思路:当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法), 那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求
- 适用于一个接口不想使用其所有的方法的情况。
代码演示
接口:
public interface IVoltage {
void output5V();
void output10V();
void output15V();
void output20V();
}
适配器:
/**
* @author cVzhanshi
* @create 2023-06-25 17:08
*/
public abstract class Adapter implements IVoltage{
@Override
public void output5V() {
}
@Override
public void output10V() {
}
@Override
public void output15V() {
}
@Override
public void output20V() {
}
}
然后使用者再根据需求重写某些方法就可以
8.5 适配器模式在SpringMVC中的运用
在DispatcherServlet类中
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
// ...
ModelAndView mv = null;
Exception dispatchException = null;
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
// 确定当前请求的处理程序,根据request来获取Handler
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
// 确定当前请求的处理程序适配器 根据handler来获取适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// ...
// Actually invoke the handler.
// 实际上调用处理程序
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// ...
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}