适配器模式
1. 概述
- 适配器模式(Adapter Design Pattern)。这个模式就是用来做适配的,将一个类的接口转换成客户希望的另外一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。(例如,现在有些手机只有一个Type-C接口,不能直接插入圆孔的有线耳机,这个时候就需要一个转接头将手机和耳机相连接,而适配器就相当于转接头)
- 适配器模式的分类:
- 类适配器:类适配器使用继承关系来实现,所以耦合度相对高一点。且要求程序员了解现有组件库中的相关组件的内部结构,所以应用相对较少些
- 对象适配器:对象适配器使用组合关系来实现。
- 接口适配器:接口适配器适用于不需要实现接口中所有方法的情况
2. 结构
- 适配器模式包含以下主要角色:
- 目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
- 适配者(Adaptee)类:它是被访问和适配的现存组件库中的组件接口。
- 适配器(Adapter)类:它是一个转换器,通过继承或引用适配者的对象,把适配者接口转换成目标接口,让客户按目标接口的格式访问适配者。
3. 类适配器模式
案例:手机,手机的Type-C接口,圆孔耳机,转接头
-
适配者类 adaptee:手机的Type-C接口
public class TypeC { public TypeC(){ System.out.println("转接头已通过Type-C连接手机"); } }
-
目标接口 target:圆孔耳机
public interface Earphone { void connectEarphone(); }
-
适配器 adapter:转接头
public class Adapter extends TypeC implements Earphone{ @Override public void connectEarphone() { System.out.println("转接头已连接耳机"); } }
-
手机
public class Phone { public void listenMusicWithoutEarphone(){ System.out.println("外放音乐"); } public void listenMusicWhitEarphone(Adapter adapter){ System.out.println("带上耳机听音乐"); } }
-
测试方法
public class Test { public static void main(String[] args) { Phone phone = new Phone();//创建一个手机 phone.listenMusicWithoutEarphone();//外放音乐 System.out.println("================="); Adapter adapter = new Adapter();//创建一个转接头(转接头已自动连接手机) adapter.connectEarphone();//转接头连接耳机 phone.listenMusicWhitEarphone(adapter); } }
-
输出结果
类适配器模式违背了合成复用原则。因为Java的单继承性,所以类适配器模式只适用于客户类有一个接口规范的情况下,反之不可用。
4. 对象适配器模式
-
与上述案例中,只有适配器类有区别
public class Adapter implements Earphone { private TypeC typeC; public Adapter(TypeC typeC){ this.typeC = typeC; } @Override public void connectEarphone() { System.out.println("转接头已连接耳机"); } }
-
测试方法
public class Test { public static void main(String[] args) { Phone phone = new Phone(); phone.listenMusicWithoutEarphone(); System.out.println("=================="); Adapter adapter = new Adapter(new TypeC()); adapter.connectEarphone(); phone.listenMusicWhitEarphone(adapter); } }
-
输出结果
5. 接口适配器模式
-
当不需要全部实现接口提供的方法时,这时我们可以使用一个抽象类作为中间件,即适配器,用这个抽象类实现接口,而在抽象类中所有的方法都进行置空,那么我们再创建抽象类的继承类,而且重写我们需要使用的那几个方法即可。(如多头充电器)
-
target接口
public interface Cable { //安卓充电线 void android(); //苹果充电线 void apple(); //Type-C充电线 void typeC(); }
-
adapter抽象适配器
public abstract class Adapter implements Cable { @Override public void android() { } @Override public void apple() { } @Override public void typeC() { } }
-
AndroidAdapter继承抽象适配器
public class AndroidAdapter extends Adapter { private Current220V current220V; public AndroidAdapter(Current220V current220V){ this.current220V = current220V; } @Override public void android() { System.out.println("安卓充电线线"); } }
-
测试方法
public class Test { public static void main(String[] args) { Current220V current220V = new Current220V(); AndroidAdapter adapter = new AndroidAdapter(current220V); adapter.android(); } }
6. 优点与缺点
- 优点:
- 能提高类的透明性和复用,现有的类复用但不需要改变。
- 目标类和适配器类解耦,提高程序的扩展性。
- 在很多业务场景中符合开闭原则。
- 缺点:
- 适配器编写过程需要全面考虑,可能会增加系统的复杂性。
- 增加代码阅读难度,降低代码可读性,过多使用适配器会使系统代码变得凌乱。
7. 使用场景
- Reader(字符流)、InputStream(字节流)的适配使用的是InputStreamReader。
- Spring框架
8. 适配器模式与装饰模式的比较
装饰器模式 | 适配器模式 | |
---|---|---|
形式 | 有层级关系,是一种非常特别的适配器模式 | 没有层级关系 |
定义 | 装饰器和被装饰器实现同一个接口,为了扩展之后保留OOP关系 | 适配器和被适配者没有必然的联系,通常是采用继承或代理的形式进行包装 |
功能 | 注重覆盖 | 注重兼容、转换 |
设计 | 前置考虑 | 后置考虑 |