以生活中充电器的例子来理解适配器:
泰国插座是用的两孔的欧标,我们就可以开个多功能转换插头(适配器)进行转换供电使用
1.请介绍什么是适配器模式:
1.1.适配器模式定义:
- 1.适配器模式是将某个类的接口转换成客户端期望的另一个接口表示,主要是兼容性,使得原本因接口不匹配不能一起工作的两个类可以协同工作。
其别名是包装器(Wrapper)
- 2.主要分三类:类适配器模式、对象适配器模式、接口适配器模式
- 3.
类适配器模式
中类之间的耦合度比后者高,且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些 - 4.
对象适配器模式
是最常用的模式
1.2 适配器模式成员:
- 1.适配器模式(Adapter)包含以下主要角色:
- 目标(Target)接口:当前系统业务
所期待的接口
,它可以是抽象类或接口
- 适配者(Adaptee)类:它是
被访问和适配的现存组件库中的组件接口
- 适配器(Adapter)类:它是一个转换器,通过
继承或引用适配者的对象
,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者
- 目标(Target)接口:当前系统业务
2.适配器模式实现:
2.1.类适配器模式实现:
a.实现方式:
- Adapter适配器类,通过继承src适配者类,实现dst目标类接口。完成src -> dst的适配
b.代码实现如下案例:
案例说明:
类图分析:
代码实现:
- 1.电源 220V:
public class Voltage220V {
public int output(){
int src = 220;
System.out.println("电压" + 220 + "伏");
return src;
}
}
- 2.目标接口:
public interface IVoltage5V {
public int output5V();
}
- 3.适配器
- 继承是为了获得220V的电源,并实现转5v电压的方法
- 实现是为了方便多个类型的充电器供手机的调用
public class VoltageAdapter extends Voltage220V implements IVoltage5V{
public int output5V() {
int srcV = output();
int dstV = srcV / 44;
return dstV;
}
}
- 4.手机:
public class Phone {
//手机充电方法:
public void charging(IVoltage5V iVoltage5V){
if(iVoltage5V.output5V() == 5){
System.out.println("充电成功,5V");
}else {
System.out.println("失败");
}
}
}
- 5.用户调用测试:
public class Client {
public static void main(String[] args) {
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
}
}
- 6.结果
电压220伏
充电成功,5V
c.类适配器模式注意事项和细节
- 1.Java 是单继承机制,所以适配器类需要继承被适配者类这一点算是一个缺点, 因为这要求目标类 必须是接口,有一定局限性;
- 2.被适配者类 的方法在 适配器 中都会暴露出来,也增加了使用的成本。
- 3.由于其继承了被适配者类,所以它可以根据需求重写被适配者类的方法,使得 适配器类 的灵活性增强了。
2.2.对象适配器模式
a.概述:
- 1.基本思路和类的适配器模式相同,
只是将 Adapter 类作修改
,不是继承 被适配者类,而是持有 被适配者类 的实例
,以解决兼容性的问题。
即:持有被适配者类,实现目标类接口,完成 被适配者 -> 目标 的适配 - 2.根据**“合成复用原则**”,在系统中尽量使用关联关系(聚合)来替代继承关系
- 3.对象适配器模式是适配器模式常用的一种
b.类图分析:
- 1,思路分析:只需修改适配器即可
c.代码实现:
- 1.220V的电源
public class Voltage220V {
public int output(){
int src = 220;
System.out.println("电压" + 220 + "伏");
return src;
}
}
- 2.目标接口:
public interface IVoltage5V {
public int output5V();
}
- 3.适配器:
public class VoltageAdapter implements IVoltage5V {
private Voltage220V voltage220V;
public VoltageAdapter(Voltage220V voltage220V){
this.voltage220V = voltage220V;
}
public int output5V() {
int dstV = 0;
if(null != voltage220V){
int srcV = voltage220V.output();
dstV = srcV / 44;
}
return dstV;
}
}
- 4.手机:
public class Phone {
public void charging(IVoltage5V iVoltage5V){
if(iVoltage5V.output5V() == 5){
System.out.println("充电成功,5V");
}else {
System.out.println("失败");
}
}
}
- 5.用户:
public class Client {
public static void main(String[] args) {
Phone phone = new Phone();
phone.charging(new VoltageAdapter(new Voltage220V()));
}
}
d.对象适配器模式注意事项和细节
- 1.对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。根据合成复用原则,使用组合替代继承,所以它解决了类适配器必须继承 被适配者类 的局限性问题,也不再要求 目标类 必须是接口。
- 2.使用成本更低,更灵活
2.3.接口适配器模式介绍
- 1.一些书籍称为:缺省适配器模式
- 2.核心思路:当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供 一个默认实现(空方法),那么该
抽象类的子类可有选择地覆盖父类的某些方法来实现需求
。 - 3.适用于一个接口不想使用其所有的方法的情况。
2.4.代码实现
-
1.思路分析
-
接口
public interface interface4 {
public void m1();
public void m2();
public void m3();
public void m4();
}
- 2.抽象类:
public abstract class AbsAdapter implements interface4{
public void m1() {
}
public void m2() {
}
public void m3() {
}
public void m4() {
}
}
- 3.调用时仅实现需要的方法:
public class Client {
public static void main(String[] args) {
AbsAdapter absAdapter = new AbsAdapter() {
@Override
public void m1() {
System.out.println("使用了m1的方法");
}
};
absAdapter.m1();
}
}
说明:这里的absAdapter的类型不是抽象类AbsAdapter,而是它的子类
- 4.测试:
public class Client {
public static void main(String[] args) {
AbsAdapter absAdapter = new AbsAdapter() {
@Override
public void m1() {
System.out.println("使用了m1的方法");
}
};
System.out.println(absAdapter.getClass());
System.out.println(absAdapter.getClass().getSuperclass());
}
}
- 5.结果:
class adapter_05.interfaceadapter.Client$1
class adapter_05.interfaceadapter.AbsAdapter
3.适配器模式应用场景:
3.1.SpringMVC中的适配器模式
源码剖析:SpringMvc中的HandlerAdapter就使用了适配器模式
a.使用 HandlerAdapter 的原因分析
- 1.可以看到处理器的类型不同,有多重实现方式,那么调用方式就不是确定的,如果需要直接调用 Controller 方法,需要调用的时候就得不断是使用 if else 来进行判断是哪一种子类然后执行。那么如果后面要扩展 Controller, 就得修改原来的代码,这样违背了
OCP
原则。
通过适配器调用controller的方法;返回ModelAndView
可以注意到HandlerAdapter适配器实际上是个接口.
这个适配器有许多实现类
b.模拟SpringMVC流程
a.类图分析:
b.代码实现:
- 1.适配器代替Controller执行相应的方法
- 2.Controller接口及其实现类
//Controller接口
public interface Controller {
}
//Controller实现类
class HttpController implements Controller{
public void doHttpHandler(){
System.out.println("HttpController");
}
}
class SimpleController implements Controller{
public void doSimpleHandler(){
System.out.println("SimpleController");
}
}
class AnnotationController implements Controller{
public void doAnnotationHandler(){
System.out.println("AnnotationController");
}
}
适配器接口及其实现类
//定义一个Adapter接口
public interface HandlerAdapter{
public boolean supports(Object handler);
public void handle(Object handler);
}
//多种适配器类
class SimpleHandlerAdapter implements HandlerAdapter{
public boolean supports(Object handler) {
return (handler instanceof SimpleController);
}
public void handle(Object handler) {
((SimpleController)handler).doSimpleHandler();
}
}
class HttpHandlerAdapter implements HandlerAdapter{
public boolean supports(Object handler) {
return (handler instanceof HttpController);
}
public void handle(Object handler) {
((HttpController)handler).doHttpHandler();
}
}
class AnnotationHandlerAdapter implements HandlerAdapter{
public boolean supports(Object handler) {
return (handler instanceof AnnotationController);
}
public void handle(Object handler) {
((AnnotationController)handler).doAnnotationHandler();
}
}
DispatchServlet处理
解释doDispatch方法,可能会有疑问,既然获得了controller对象,为啥不直接调用它的处理方法,而是要获取到适配器,通过适配器调用controller的处理方法。
注意看每个Controller处理方法的名字不一样,那在执行的时候就需要if else判断是哪一种子类然后执行。那么如果后面要扩展 Controller, 就得修改原来的代码,这样违背了 OCP 原则。
public class DispatchServlet {
public static List<HandlerAdapter> handlerAdapters = new ArrayList();
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();
// 得到对应适配器
HandlerAdapter adapter = getHandlerAdapter(controller);
// 通过适配器执行对应的controller对应方法
adapter.handle(controller);
}
public HandlerAdapter getHandlerAdapter(Controller controller) {
//遍历:根据得到的controller(handler), 返回对应适配器
for (HandlerAdapter adapter : handlerAdapters) {
if (adapter.supports(controller)) {
return adapter;
}
}
return null;
}
public static void main(String[] args) {
new DispatchServlet().doDispatch(); // http...
}
}
说明:SpringMVC定义了一个适配接口,使得每一种Controller有一种对应的适配器实现类,适配器代替Controller执行相应的方法,扩展Controller时,只需要增加一个适配器类就完成了SpringMVC的扩展。