前言:
适配器模式(Adapter Pattern)是作为两个不兼容的接口之间的桥梁。这种类型的设计模式属于结构型模式,它结合了两个独立接口的功能。
适配器模式通过定义一个新的接口(对要实现的功能加以抽象),和一个实现该接口的Adapter(适配器)类来透明地调用外部组件。这样替换外部组件时,最多只要修改几个Adapter类就可以了,其他源代码都不会受到影响。
以手机为例子,每一种机型都自带有从电器,有一天自带充电器坏了,而且市场没有这类型充电器可买了,怎么办?万能充电器就可以解决,这个万能充电器就是适配器。
适配器模式有两种形式,一种是类的适配,另一种是对象的适配。
介绍:
-
意图:将一个类的接口转换成客户希望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
-
主要解决:主要解决在软件系统中,常常要将一些"现存的对象"放到新的环境中,而新环境要求的接口是现对象不能满足的。
-
何时使用:
- 系统需要使用现有的类,而此类的接口不符合系统的需要。
- 想要建立一个可以重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作,这些源类不一定有一致的接口。
- 通过接口转换,将一个类插入另一个类系中。(比如老虎和飞禽,现在多了一个飞虎,在不增加实体的需求下,增加一个适配器,在里面包容一个虎对象,实现飞的接口。)
-
如何解决:继承或依赖(推荐)。
-
关键代码:适配器继承或依赖已有的对象,实现想要的目标接口。
-
优点
- 可以让任何两个没有关联的类一起运行。
- 提高了类的复用。
- 增加了类的透明度。
- 灵活性好。
-
缺点:
- 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
- 由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
类的适配:
这种适配器由三种角色组成:
-
目标(Target)角色:这就是所期待得到的接口。注意:由于这里讨论的是类适配器模式,因此目标不可以是类。
-
源(Adapee)角色:现在需要适配的接口。
-
适配器(Adaper)角色:适配器类是本模式的核心。适配器把源接口转换成目标接口。显然,这一角色不可以是接口,而必须是具体类。
对象的适配:
对象的适配依赖于对象的组合,而不是类适配中的继承。
示例:
1、类适配器
**故事情景:**手机充电用手机充电器充电,耳机用耳机充电器充电,现在要实现给手机充电的同时也能给耳机充电。
首先来一个IPhone的充电器类(Adaptee角色):
/**
* 充电器接口
*/
public interface ICharge {
void charge();
}
/**
* 苹果手机充电器需要适配的角色(Adapee)
*/
public class AppleCharger implements ICharge{
public void charge(){
System.out.println("The ApplePhone is charging ...");
}
}
现在,要对这个特殊的充电器进行适配,上个适配的接口(Target目标角色):
/**
* 要对这个特殊的充电器进行适配,适配器接口(Target角色)
*/
public interface IChargeAdapter {
void charge();
}
创建类的适配器:
/**
* 多接头充电器,支持一边充手机,一边充耳机(Adaper)
*/
public class MultipleJointsCharger extends AppleCharger implements IChargeAdapter{
@Override
public void charge() {
super.charge();
System.out.println("The Headset is charging ...");
}
}
启动类:
/**
* 启动类
*/
@SpringBootApplication
public class StartClass {
public static void main(String[] args){
MultipleJointsCharger multipleJointsCharger = new MultipleJointsCharger();
multipleJointsCharger.charge();
}
}
控制台:
2、对象适配器
故事情景 :苹果手机用苹果充电器充电,安卓手机用安卓手机充电,现在需要一个万能充电器能给这两种手机充电。
创建两个充电器类(Adaptee源角色):
/**
* 充电器接口
*/
public interface ICharge {
void charge();
}
/**
* 安卓手机充电器需要适配的角色(Adaptee)
*/
public class AndroidCharger implements ICharge{
@Override
public void charge() {
System.out.println("The AndroidPhone is charging ...");
}
}
/**
* 苹果手机充电器需要适配的角色(Adaptee)
*/
public class AppleCharger implements ICharge{
public void charge(){
System.out.println("The ApplePhone is charging ...");
}
}
现在,要对这个特殊的充电器进行适配,上个适配的接口(Target目标角色):
/**
* 要对这个特殊的充电器进行适配,适配器接口(Target角色)
*/
public interface IChargeAdapter {
void charge();
}
创建类的适配器:
/**
* 万能充电器,类的适配器(Adaper)
*/
public class UniversalCharger implements IChargeAdapter{
private ICharge charge;
public UniversalCharger(ICharge charge){
this.charge = charge;
}
@Override
public void charge() {
charge.charge();
}
}
启动类:
/**
* 启动类
*/
@SpringBootApplication
public class StartClass {
public static void main(String[] args){
UniversalCharger universalCharger = new UniversalCharger(new AppleCharger());
universalCharger.charge();
UniversalCharger universalCharger1 = new UniversalCharger(new AndroidCharger());
universalCharger1.charge();
}
}
控制台:
总结:
-
类的适配器模式:当希望将一个类转换成满足另一个新接口的类时,可以使用类的适配器模式,创建一个新类,继承原有的类,实现新的接口即可。
-
对象的适配器模式:当希望将一个对象转换成满足另一个新接口的对象时,可以创建一个包装类,持有原类的一个实例,在包装类的方法中,调用实例的方法就行