字节跳动飞书内推!
北京、杭州、武汉、广州、深圳、上海,六大城市等你来投。
感兴趣的朋友可以私我咨询&内推,也可以通过链接直接投递!
海量HC,极速响应,快来和我成为同事吧。
今日头条、抖音、Tik Tok也可以内推~
点击进入我的博客
3.1 适配器模式
适配器模式把一个类的接口变换成客户端所期待的另一种接口,使得原本因接口不匹配而无法在一起工作的两个类能够在一起工作。
3.1.1 类的适配器结构
- 目标(Target)角色:这就是所期待得到的接口,由于是类适配器模式,因此目标不可以是类。
- 源(Adaptee)角色:现有需要适配的接口。
- 适配器(Adapter)角色:适配器类是本模式的核心,必须是具体类。
interface Target {
void operation1();
void operation2();
}
class Adaptee {
public void operation1() {}
}
class Adapter extends Adaptee implements Target {
@Override
public void operation2() {}
}
类的适配器
- 类的适配器模式把被的类的API转换成目标类的API。
- 是通过继承实现的。
类的适配器效果
- 使用一个具体类把源适配到目标中。这样如果源以及源的子类都使用此类适配,就行不通了。
- 由于适配器是源的子类,因此可以在适配器中重写源的一些方法。
- 由于引进了一个适配器类,因此只有一个线路到达目标类,是问题得到简化。
3.1.2 对象的适配器结构
- 目标(Target)角色:这就是所期待得到的接口,因此目标可以是具体或抽象的类。
- 源(Adaptee)角色:现有需要适配的接口。
- 适配器(Adapter)角色:适配器类是本模式的核心,必须是具体类。
interface Target {
void operation1();
void operation2();
}
class Adaptee {
public void operation1() {}
}
class Adapter implements Target {
private Adaptee adaptee;
public Adapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void operation1() {
adaptee.operation1();
}
@Override
public void operation2() {
// do something
}
}
对象的适配器
- 对象的适配器是通过依赖实现的
- 推荐使用该方法
对象的适配器效果
- 一个适配器可以把多种不同的源适配到同一个目标,即同一个适配器可以把源类和它的子类都适配到目标接口
- 与类的适配器模式相比,要想置换源类的方法就不容易。
- 增加新的方法方便的多
3.1.3 细节
使用场景
- 系统需要使用现有的类,而此类的接口不符合系统的需要
- 想要建立一个可以重复使用的类,用于与一些彼此间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
- 对对象的适配器儿模式而言,在设计里,需要改变多个已有的子类的接口,如果使用类的适配器模式,就需要针对每个子类做一个适配器类,这不太实际。
优点
- 可以让任何两个没有关联的类一起运行。
- 提高了类的复用。
- 增加了类的透明度。
- 灵活性好。
缺点
- 过多地使用适配器,会让系统非常零乱,不易整体进行把握。比如,明明看到调用的是 A 接口,其实内部被适配成了 B 接口的实现,一个系统如果太多出现这种情况,无异于一场灾难。因此如果不是很有必要,可以不使用适配器,而是直接对系统进行重构。
- 由于 JAVA 至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是抽象类。
注意点
- 目标接口可以忽略,此时目标接口和源接口实际上是相同的
- 适配器类可以是抽象类
- 可以有带参数的适配器模式
3.1.4 一个充电器案例
// 充电器只能接受USB接口
public class Charger {
public static void main(String[] args) throws Exception{
USB usb = new SuperAdapter(new TypeC());
connect(usb);
usb = new SuperAdapter(new Lightning());
connect(usb);
}
public static void connect(USB usb) {
usb.power();
usb.data();
}
}
// 充电器的接口都是USB的,假设有两个方法分别是电源和数据
interface USB {
void power();
void data();
}
// IOS的Lightning接口
class Lightning {
void iosPower() {
System.out.println("IOS Power");
}
void iosData() {
System.out.println("IOS Data");
}
}
// TYPE-C接口
class TypeC {
void typeCPower() {
System.out.println("TypeC Power");
}
void typeCData() {
System.out.println("TypeC Data");
}
}
// 超级适配器,可以适配多种手机机型
class SuperAdapter implements USB {
private Object obj;
public SuperAdapter(Object obj) {
this.obj = obj;
}
@Override
public void power() {
if(obj.getClass() == Lightning.class) {
((Lightning)obj).iosPower();
} else if(obj.getClass() == TypeC.class) {
((TypeC)obj).typeCPower();
}
}
@Override
public void data() {
if(obj.getClass() == Lightning.class) {
((Lightning)obj).iosData();
} else if(obj.getClass() == TypeC.class) {
((TypeC)obj).typeCData();
}
}
}
3.2 缺省适配模式
缺省适配模式为一个接口提供缺省实现,这样子类型可以从这个缺省实现进行扩展,而不必从原有接口进行扩展。
3.2.1 缺省适配模式结构
简单的例子
- 下面程序中,
Monk
接口定义了两个方法,于是它的子类必须实现这两个方法。 - 但出现了一个
LuZhiShen
,他只能实现一部分方法,另一部分方法无法实现 - 所以需要一个抽象的适配类
MonkAdapter
实现此Monk
接口,此抽象类给接口所有方法都提供一个空的方法,LuZhiShen
只需要继承该适配类即可。
// 和尚
interface Monk {
void practiceKungfu();
void chantPrayer();
}
abstract class MonkAdapter implements Monk {
@Override
public void practiceKungfu() {}
@Override
public void chantPrayer() {}
}
class LuZhiShen extends MonkAdapter {
@Override
public void practiceKungfu() {
System.out.println("拳打镇关西");
}
}
3.2.2 细节
使用场景
- 任何时候不准备实现一个接口中所有方法的时候
作用
缺省适配器模式可以使所需要的类不必实现不需要的接口。
核心点
- 缺省适配的类必须是抽象类,因为这个类不应当被实例化
- 缺省适配的类提供的方法必须是具体的方法,而不是抽象的方法。