设计模式学习笔记 - 适配器模式


一、适配器模式介绍


适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作,其别名为包装器(Wrapper)。

适配器模式属于结构型模式。

适配器模式主要分为三类:类适配器模式对象适配器模式接口适配器模式

工作原理:
(1)适配器模式:将一个类的接口转换成另一种接口,让原本接口不兼容的类可以兼容。
(2)用户调用适配器转化出来的目标接口方法,然后适配器再调用被适配者的相关接口方法。
(3)从用户的角度看不到被适配者,是解耦的。用户收到反馈结果,感觉只是和目标接口交互。

在这里插入图片描述

二、类适配器模式

1、类适配器模式介绍


基本介绍:Adapter类,通过继承src类和实现dst接口,完成src到dst的适配。

2、类适配器模式应用实例


以生活中充电器的例子来讲解适配器,充电器本身相当于Adapter,220V交流电相当于src(被适配者),5V直流电相当于dst(目标最终需要的输出)。

使用类适配器模式实现应用实例。

  • 类图:
    在这里插入图片描述

  • 代码实现:

package com.etc.design.dapter.classadapter;

// 被适配的类
public class Voltage220V {
   //输出220V的电压
   public int output220V() {
      int src = 220;
      System.out.println("电压=" + src + "伏");
      return src;
   }
}
package com.etc.design.dapter.classadapter;

// 适配接口
public interface IVoltage5V {
	public int output5V();
}
package com.etc.design.dapter.classadapter;

// 适配器类
public class VoltageAdapter extends Voltage220V implements IVoltage5V {
	@Override
	public int output5V() {
		//获取到220V电压
		int srcV = output220V();
		//转成 5v
		int dstV = srcV / 44 ;
		return dstV;
	}
}
package com.etc.design.dapter.classadapter;

public class Phone {
	// 充电
	public void charging(IVoltage5V iVoltage5V) {
		if(iVoltage5V.output5V() == 5) {
			System.out.println("电压为5V, 可以充电。");
		} else if (iVoltage5V.output5V() > 5) {
			System.out.println("电压大于5V, 不能充电。");
		}
	}
}
package com.etc.design.dapter.classadapter;

public class Client {
	public static void main(String[] args) {
		System.out.println("----类适配器模式----");
		Phone phone = new Phone();
		phone.charging(new VoltageAdapter());
	}
}

在这里插入图片描述

3、类适配器模式注意事项和细节


(1)由于Java是单继承机制,所以类适配器需要继承src类这一点算是一个缺点, 因为这要求dst必须是接口,有一定局限性。
(2)src类的方法在Adapter中都会暴露出来,也增加了使用的成本。
(3)由于Adapter继承了src类,所以它可以根据需求重写src类的方法,使得Adapter的灵活性增强了。

三、对象适配器模式

1、对象适配器模式介绍


(1)基本思路和类的适配器模式相同,只是将Adapter类稍作修改,不是继承src类,而是持有src类的实例对象,以解决兼容性的问题。 即:持有src类的实例对象,实现dst接口,完成src到dst的适配。
(2)根据”合成复用原则“,在系统中尽量使用关联关系(聚合)来替代继承关系。
(3)对象适配器模式是适配器模式常用的一种。

2、对象适配器模式应用实例


以生活中充电器的例子来讲解适配器,充电器本身相当于Adapter,220V交流电相当于src(被适配者),5V直流电相当于dst(目标最终需要的输出)。

使用对象适配器模式实现应用实例:

  • 类图:
    在这里插入图片描述

  • 实现代码:

package com.etc.design.dapter.objectadapter;

// 适配接口
public interface IVoltage5V {
	public int output5V();
}
package com.etc.design.dapter.objectadapter;

// 被适配的类
public class Voltage220V {
	//输出220V的电压
	public int output220V() {
		int src = 220;
		System.out.println("电压=" + src + "伏");
		return src;
	}
}
package com.etc.design.dapter.objectadapter;

public class Phone {
	// 充电
	public void charging(IVoltage5V iVoltage5V) {
		if(iVoltage5V.output5V() == 5) {
			System.out.println("电压为5V, 可以充电。");
		} else if (iVoltage5V.output5V() > 5) {
			System.out.println("电压大于5V, 不能充电。");
		}
	}
}
package com.etc.design.dapter.objectadapter;

// 适配器类
public class VoltageAdapter implements IVoltage5V {
	// 关联关系-聚合关系
	private Voltage220V voltage220V;

	// 通过构造器,传入一个Voltage220V实例
	public VoltageAdapter(Voltage220V voltage220v) {
		this.voltage220V = voltage220v;
	}
    
	@Override
	public int output5V() {
		int dst = 0;
		if(null != voltage220V) {
			// 获取220V电压
			int src = voltage220V.output220V();
			System.out.println("使用对象适配器,进行适配。");
			dst = src / 44;
			System.out.println("适配完成,输出的电压为=" + dst);
		}
		return dst;
	}
}
package com.etc.design.dapter.objectadapter;

public class Client {
	public static void main(String[] args) {
		System.out.println("----对象适配器模式----");
		Phone phone = new Phone();
		phone.charging(new VoltageAdapter(new Voltage220V()));
	}
}

在这里插入图片描述

3、对象适配器模式注意事项和细节


(1)对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。根据合成复用原则,使用组合关系代替继承关系, 所以它解决了类适配器必须继承src的局限性问题,也不再要求dst必须是接口。
(2)使用成本更低、更灵活。

四、接口适配器模式

1、接口适配器模式介绍


(1)接口适配器模式也称为缺省适配器模式(Default Adapter Pattern)
(2)核心思路:当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求。
(3)适用于不想使用接口中所有的方法的情况。

2、接口适配器模式应用实例

  • 类图:
    在这里插入图片描述

  • 实现代码:

package com.etc.design.dapter.interfaceadapter;

public interface InterfaceDemo {
    public void method1();
    public void method2();
    public void method3();
    public void method4();
}
package com.etc.design.dapter.interfaceadapter;

// 在AbstractAdapterDemo将InterfaceDemo的方法进行默认实现(空方法)
public abstract class AbstractAdapterDemo implements InterfaceDemo{
    // method1方法默认实现(空方法)
    public void method1() {}
    // method2方法默认实现(空方法)
    public void method2() {}
    // method3方法默认实现(空方法)
    public void method3() {}
    // method4方法默认实现(空方法)
    public void method4() {}
}
package com.etc.design.dapter.interfaceadapter;

public class Client {
    public static void main(String[] args) {
        AbstractAdapterDemo abstractAdapterDemo = new AbstractAdapterDemo() {
            //只需要去重写需要到使用的接口方法
            @Override
            public void method1() {
                System.out.println("使用了method1方法");
            }
        };
        abstractAdapterDemo.method1();
    }
}

在这里插入图片描述

五、适配器模式在SpringMVC框架应用的源码剖析


(1)SpringMVC中的HandlerAdapter使用了适配器模式。

  • HandlerAdapter有多个实现子类,每一种controller有一种对应的适配器实现类,每种controller都有不同的实现方式。
    在这里插入图片描述

(2)使用HandlerAdapter原因分析:

  • 如果有多种类型的处理器,则有多种实现方式,那么调用方式就不确定。如果需要直接调用Controller方法,调用的时候就需要不断使用if else语句来进行判断是哪一种子类,然后再执行。如果后面需要扩展Controller,就要修改原来的代码,这样违背了OCP原则。

(3)使用适配器设计模式分析:

  • Spring定义了一个适配接口,使得每一种Controller都有一种对应的适配器实现类。
  • 适配器代替controller执行相应的方法。
  • SpringMVC扩展Controller时,只需要增加一个适配器类。

具体说明:DispatcherServlet方法的doDispatch方法中传入request,将request通过HandlerMapping映射到对应的Controller,通过controller获取相对应的适配器,通过调用该适配器的handle方法(该适配器的handle方法会调用Controller的handle方法)并返回ModelAndVew。

@SuppressWarnings("serial")
public class DispatcherServlet extends FrameworkServlet {
	...
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		ModelAndView mv = null;
		// Handler可看成一个controller
		HandlerExecutionChain mappedHandler = null;
		......
		processedRequest = checkMultipart(request);
		......
		// 1、通过HandlerMapping来映射Controller
		mappedHandler = getHandler(processedRequest);
		......
		// 2、通过controller获取相对应的适配器
		HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
		......
		// 3、通过适配器调用controller的handle方法并返回ModelAndVew
		mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
		......
	}
	......
	// 通过controller获取相对应的适配器
	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");
	}
	......
}

// Spring创建了一个适配器接口(HandlerAdapter),适配器接口有多个适配器实现类
public interface HandlerAdapter {

	boolean supports(Object handler);
	
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
	
	long getLastModified(HttpServletRequest request, Object handler);
}
// 例如HttpRequestHandlerAdapter适配器实现类(HandlerAdapter的实现子类之一)
public class HttpRequestHandlerAdapter implements HandlerAdapter {
	@Override
	public boolean supports(Object handler) {
		return (handler instanceof HttpRequestHandler);
	}
	// handle方法调用的是对应controller的handle方法
	@Override
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		((HttpRequestHandler) handler).handleRequest(request, response);
		return null;
	}
	@Override
	public long getLastModified(HttpServletRequest request, Object handler) {
		if (handler instanceof LastModified) {
			return ((LastModified) handler).getLastModified(request);
		}
		return -1L;
	}
}

(4)简写模拟SpringMVC通过适配器设计模式获取到对应的Controller的源码。

  • 类图:
    在这里插入图片描述

  • 实现代码:

package com.etc.design.dapter.springmvc;

// Controller有多个实现子类
public interface Controller {

}

class HttpController implements Controller {
	public void doHttpHandler() {
		System.out.println("http...");
	}
}

class SimpleController implements Controller {
	public void doSimplerHandler() {
		System.out.println("simple...");
	}
}

class AnnotationController implements Controller {
	public void doAnnotationHandler() {
		System.out.println("annotation...");
	}
}
package com.etc.design.dapter.springmvc;

// 定义一个Adapter适配器接口
public interface HandlerAdapter {
	public boolean supports(Object handler);

	public void handle(Object handler);
}

// 多种Adapter适配器类
class SimpleHandlerAdapter implements HandlerAdapter {
	public void handle(Object handler) {
		((SimpleController) handler).doSimplerHandler();
	}
	public boolean supports(Object handler) {
		return (handler instanceof SimpleController);
	}
}

class HttpHandlerAdapter implements HandlerAdapter {
	public void handle(Object handler) {
		((HttpController) handler).doHttpHandler();
	}
	public boolean supports(Object handler) {
		return (handler instanceof HttpController);
	}

}

class AnnotationHandlerAdapter implements HandlerAdapter {
	public void handle(Object handler) {
		((AnnotationController) handler).doAnnotationHandler();
	}
	public boolean supports(Object handler) {
		return (handler instanceof AnnotationController);
	}
}
package com.etc.design.dapter.springmvc;

import java.util.ArrayList;
import java.util.List;

public class DispatchServlet {
	public static List<HandlerAdapter> handlerAdapters = new ArrayList<HandlerAdapter>();

	public DispatchServlet() {
		handlerAdapters.add(new AnnotationHandlerAdapter());
		handlerAdapters.add(new HttpHandlerAdapter());
		handlerAdapters.add(new SimpleHandlerAdapter());
	}

	public void doDispatch() {
		// 此处模拟SpringMVC从request取handler(可以看成controller)的对象
		 HttpController controller = new HttpController();
		// AnnotationController controller = new AnnotationController();
		// SimpleController controller = new SimpleController();
		// 通过controller得到对应适配器
		HandlerAdapter adapter = getHandler(controller);
		// 通过适配器执行对应的controller对应方法
		adapter.handle(controller);
	}

	public HandlerAdapter getHandler(Controller controller) {
		//遍历handlerAdapters集合, 根据得到的controller, 返回对应适配器
		for (HandlerAdapter adapter : this.handlerAdapters) {
			if (adapter.supports(controller)) {
				return adapter;
			}
		}
		return null;
	}

	public static void main(String[] args) {
		// 测试
		new DispatchServlet().doDispatch();
	}
}

六、适配器模式的注意事项和细节


(1)适配器模式的三种命名方式,是根据src是以怎样的形式给到Adapter(在Adapter里的形式)来命名的。

  • 类适配器:以类的形式给到Adapter,Adapter中继承src类。
  • 对象适配器:以对象的形式给到Adapter,Adapter中持有src的对象实例。
  • 接口适配器:以接口的形式给到Adapter,Adapter中实现src接口。

(2)Adapter模式最大的作用是将原本不兼容的接口融合在一起工作。
(3)在实际开发中,适配器模式实现起来不拘泥于三种经典形式。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值