您当前的位置: 首页 > 

Dongguo丶

暂无认证

  • 2浏览

    0关注

    472博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

设计模式之适配器模式

Dongguo丶 发布时间:2019-01-13 18:47:42 ,浏览量:2

模式定义

适配器模式就是将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

在适配器模式中,我们可以定义一个包装类,包装不兼容接口的对象,这个包装类就是适配器,它所包装的对象就是适配者。

适配器提供给客户需要的接口,适配器的实现就是将客户的请求转换成对适配者的相应的接口的引用。也就是说,当客户调用适配器的方法时,适配器方法内部将调用适配者的方法,客户并不是直接访问适配者的,而是通过调用适配器方法访问适配者。因为适配器可以使互不兼容的类能够“合作愉快”。

1) 适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主的目的是

2) 适配器模式属于结构型模式

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

工作原理

1) 适配器模式:将一个类的接口转换成另一种接口.让原本接口不兼容的类可以兼容

2) 从用户的角度看不到被适配者,是解耦的

3) 用户调用适配器转化出来的目标接口方法,适配器再调用被适配者的相关接口方法

4) 用户收到反馈结果,感觉只是和目标接口交互

模式结构

img

这个适配器模式充满着良好的OO设计原则:使用对象组合,以修改的接口包装别适配者。而且这样做还有一个优点,被适配者的任何子类,都可以搭配适配器使用。

适配器模式有如下四个角色:

Target:目标抽象类

Adapter:适配器类

Adaptee:适配者类

Client:客户类

我们生活上的电压一般为220v,而笔记本的电压大致在18v左右,

笔记本如果直接连生活用电是肯定是不行的,笔记本电源安装了电源适配器,这样笔记本才可以正常的充电

类适配器模式

思路分析(类图)

package com.dongguo.adapter.classadapter;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-12:56
 * @description: 被适配的类
 */
public class Voltage220V {
    //输出220V 的电压
    public int output220V() {
        int src = 220;
        System.out.println("电压=" + src + "伏");
        return src;
    }
}

package com.dongguo.adapter.classadapter;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-12:57
 * @description: 适配接口
 */

public interface IVoltage18V {
    public int output18V();
}
package com.dongguo.adapter.classadapter;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-12:57
 * @description:
 */
public class Computer {
    //充电
    public void charging(IVoltage18V iVoltage18V) {
        if(iVoltage18V.output18V() == 18) {
            System.out.println("电压为18V, 可以充电~~");
        } else if (iVoltage18V.output18V() > 18) {
            System.out.println("电压大于18V, 不能充电~~");
        }
    }
}

直接使用220v的电压笔记本会烧坏的,创建一个适配器

package com.dongguo.adapter.classadapter;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-12:58
 * @description: 适配器类
 */
public class VoltageAdapter extends Voltage220V implements IVoltage18V {
    @Override
    public int output18V() {

        //获取到220V 电压
        int srcV = output220V();
        int dstV = srcV / 12 ; //转成18v
        return dstV;
    }
}
package com.dongguo.adapter.classadapter;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-12:58
 * @description:
 */
public class Client {
    public static void main(String[] args) {
        System.out.println(" === 类适配器模式====");
        Computer computer = new Computer();
        computer.charging(new VoltageAdapter());
    }
}
运行结果
 === 类适配器模式====
电压=220
电压为18V, 可以充电~~

适配器模式通过定义一个接口(对要实现的功能加以抽象),和一个实现该接口的适配器类来透明的调用外部组件,如果要更改外部组件时,只需要修改适配器类就可以。

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

1) Java 是单继承机制,所以类适配器需要继承Voltage220V 类这一点算是一个缺点, 因为这要求IVoltage18V 必须是接口,有一定局限性;

2) Voltage220V 类的方法在VoltageAdapter 中都会暴露出来,也增加了使用的成本。

3) 由于其VoltageAdapter 继承了Voltage220V 类,所以它可以根据需求重写Voltage220V 类的方法,使得VoltageAdapter 的灵活性增强了

对象适配器模式

基本思路和类的适配器模式相同,只是将VoltageAdapter 类作修改,不是继承Voltage220V 类,而是持有Voltage220V 类的实例,以解决

兼容性的问题。即:持有Voltage220V 类,实现IVoltage18V 类接口,完成Voltage220V ->IVoltage18V 的适配

2) 根据“合成复用原则”,在系统中尽量使用关联关系(聚合)来替代继承关系。

3) 对象适配器模式是适配器模式常用的一种

以生活中充电器的例子来讲解适配器,电源适配器本身相当于VoltageAdapter,220V 交流电相当于Voltage220V (即被适配者),我们

的目IVoltage18V (即目标)是18V 直流电,使用对象适配器模式完成。

2) 思路分析(类图):只需修改适配器即可, 如下:

package com.dongguo.adapter.objectadapter;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-13:00
 * @description:
 */
public class Voltage220V {
    //输出220V 的电压
    public int output220V() {
        int src = 220;
        System.out.println("电压=" + src + "伏");
        return src;
    }
}

package com.dongguo.adapter.objectadapter;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-13:03
 * @description:
 */

public interface IVoltage18V {
    public int output18V();
}
package com.dongguo.adapter.objectadapter;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-13:03
 * @description:
 */
public class VoltageAdapter implements IVoltage18V {
    private Voltage220V voltage220V; // 关联关系-聚合

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

/**
 * @author Dongguo
 * @date 2021/8/22 0022-13:10
 * @description:
 */
public class Computer {
    //充电
    public void charging(IVoltage18V iVoltage18V) {
        if(iVoltage18V.output18V() == 18) {
            System.out.println("电压为18V, 可以充电~~");
        } else if (iVoltage18V.output18V() > 18) {
            System.out.println("电压大于18V, 不能充电~~");
        }
    }
}
package com.dongguo.adapter.objectadapter;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-13:10
 * @description:
 */
public class Client {
    public static void main(String[] args) {

        System.out.println(" === 对象适配器模式====");
        Computer computer = new Computer();
        computer.charging(new VoltageAdapter(new Voltage220V()));
    }
}
运行结果
 === 对象适配器模式====
电压=220伏
使用对象适配器,进行适配~~
适配完成,输出的电压为=18
电压为18V, 可以充电~~

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

1) 对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。

根据合成复用原则,使用组合替代继承, 所以它解决了类适配器必须继承Voltage220V  的局限性问题,也不再要求IVoltage18V 必须是接口。

2) 使用成本更低,更灵活。

接口适配器模式

1) 一些书籍称为:适配器模式(Default Adapter Pattern)或缺省适配器模式。

2) 核心思路:当不需要全部实现接口提供的方法时,可先设计一个抽象类实现接口,并为该接口中每个方法提供

一个默认实现(空方法),那么该抽象类的子类可有选择地覆盖父类的某些方法来实现需求

3) 适用于一个接口不想使用其所有的方法的情况。

package com.dongguo.adapter.interfaceadapter;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-13:11
 * @description:
 */
public interface Interface4 {
    public void m1();
    public void m2();
    public void m3();
    public void m4();
}
package com.dongguo.adapter.interfaceadapter;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-13:12
 * @description: 在AbsAdapter 我们将Interface4 的方法进行默认实现
 */
public abstract class AbsAdapter implements Interface4 {
    //默认实现
    public void m1() {
    }
    public void m2() {
    }
    public void m3() {
    }
    public void m4() {
    }
}
package com.dongguo.adapter.interfaceadapter;

/**
 * @author Dongguo
 * @date 2021/8/22 0022-13:12
 * @description:
 */
public class Client {
    public static void main(String[] args) {
        AbsAdapter absAdapter = new AbsAdapter() {
            //只需要去覆盖我们需要使用接口方法
            @Override
            public void m1() {
                System.out.println("使用了m1 的方法");
            }
        };
        absAdapter.m1();
    }
}
运行结果
使用了m1 的方法

适配器模式在JDK中的应用及解读

适配器模式可能是最不好理解的一种写法。适配器模式的写法很多,写法越多、模式越不好理解,就越应该抓住模式的核心,像适配器模式的核心就是"把一个类的接口变换成客户端所期待的另一种接口",所以我们可以看一下InputStreamReader和OutputStreamWriter。

比方说InputStreamReader

创建InputStreamReader对象的时候必须在构造函数中传入一个InputStreamReader实例,

package java.io;

import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import sun.nio.cs.StreamDecoder;


public class InputStreamReader extends Reader {

    private final StreamDecoder sd;

    public InputStreamReader(InputStream in) {
        super(in);
        try {
            sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
        } catch (UnsupportedEncodingException e) {
            // The default encoding should always be available
            throw new Error(e);
        }
    }

    ...
}

然后InputStreamReader的作用就是将InputStream适配到Reader。很显然,适配器就是InputStreamReader,源角色就是InputStream代表的实例对象,目标接口就是Reader类。

package java.io;


public abstract class Reader implements Readable, Closeable {

 
    protected Object lock;

    protected Reader() {
        this.lock = this;
    }

 
    protected Reader(Object lock) {
        if (lock == null) {
            throw new NullPointerException();
        }
        this.lock = lock;
    }

    ...

}

OutputStreamWriter也是类似的方式。

适配器模式在SpringMVC 框架应用

SpringMvc 中的HandlerAdapter, 就使用了适配器模式,

springMVC处理请求流程图

 SpringMVM 中的 HandlerAdapter(上图的第4步), 就使用了适配器模式;

DispatcherServlet类下有一个获得处理器适配器的方法:

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
	if (this.handlerAdapters != null) {
        //遍历所有的处理器适配器,找到支持处理当前handler的处理器适配器(HandlerAdapter是一个接口,其实返回的是一个实现HandlerAdapter的子类)
		for (HandlerAdapter adapter : this.handlerAdapters) {
			if (adapter.supports(handler)) {
				return adapter;
			}
		}
	}
	throw new ServletException("No adapter for handler [" + handler +
			"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

HandlerAdapter接口:

public interface HandlerAdapter {

	boolean supports(Object handler);

	@Nullable
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

	long getLastModified(HttpServletRequest request, Object handler);

}

实现HandlerAdapter子类有这些,使得每一种controller有对应的适配器实现类,每种controller有不同的实现方式

 

使用 HandlerAdapter 的原因分析:

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

优点

 1.  将目标类和适配者类解耦,通过使用适配器让不兼容的接口变成了兼容,让客户从实现的接口解耦。

 2. 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。

 3. 灵活性和扩展性都非常好在不修改原有代码的基础上增加新的适配器类,符合“开闭原则”。

使用场景
  1. 系统需要使用现有的类,而这些类的接口不符合系统的需要。

  2. 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类

总结

1、当我们需要使用的一个现有的类,但是他的接口并不符合我们的需求时,我们可以使用适配器模式。

2、适配器模式分为类适配器和对象适配器,其中类适配器需要用到多重继承。

关注
打赏
1638062488
查看更多评论
立即登录/注册

微信扫码登录

0.0433s