前言介绍
接下里介绍的是 Java 的设计模式之一:适配器模式
如果你是第一次出国到美国旅行,你会发现美国电源插头和插座规范与中国不同。这时你须要购买美国的电源插头与插座。
要是第二次出国到德国,到了德国又发现美国插头和德国插座不匹配
这时咱们须要购买同时提供美国规范插座和欧洲规范插头的电源适配器能够解决这个难题
一、什么是适配器模式
适配器模式 (Adapter Pattern): 将某个类的接口转换成客户端冀望的另一个接口示意
。
主的目标是 兼容性,让本来因接口不匹配不能一起工作的两个类能够协同工作
。其别名为包装器(Wrapper)
适配器模式属于结构型模式
次要分为三类:类适配器模式、对象适配器模式、接口适配器模式
二、工作原理
适配器模式:将一个类的接口转换成另一种接口. 让本来接口不兼容的类能够兼容
从用户的角度 看不到被适配者,是解耦的
用户 调用适配器转化进去的指标接口办法,适配器再调用被适配者的相干接口办法
用户收到 反馈后果,感觉只是和指标接口交互
,如图
三、类适配器模式
有一个 Adapter 通过继承 src 类,实现 dst 类接口,实现 src->dst 的适配。
利用实例阐明
以生存中充电器的例子来解说适配器:
- 充电器自身相当于 Adapter
- 220V 交流电相当于 src (即被适配者)
- 咱们的目 dst(即指标)是 5V 直流电压
依据类适配器的思路,咱们的类图是这样的状况
依照咱们的思路,先创立一个 220v 的电源类输入电压
// 被适配的类
class Voltage220V {
// 输入 220V 的电压
public int output220V() {
int src = 220;
System.out.println("电压 =" + src + "伏");
return src;
}
}
接下里咱们提供手机应用的 5v 电压,进行输入
// 适配接口
interface IVoltage5V {public int output5V();
}
这样咱们就须要一个适配器类将 220v 降压转为 5v 的电压
// 适配器类
class VoltageAdapter extends Voltage220V implements IVoltage5V {
@Override
public int output5V() {
// 获取到 220V 电压
int srcV = output220V();
int dstV = srcV / 44 ; // 降压解决转成 5v
return dstV;
}
}
接下里咱们就能够让手机通过适配器,进行充电啦
class Phone {
// 充电
public void charging(IVoltage5V iVoltage5V) {if (iVoltage5V.output5V() == 5) {System.out.println("电压为 5V, 能够充电~~");
} else if (iVoltage5V.output5V() > 5) {System.out.println("电压大于 5V, 不能充电~~");
}
}
}
接下来让咱们看看这样 demo 是怎么进行的呢?
public static void main(String[] args) {System.out.println("=== 类适配器模式 ====");
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
}
运行后果如下:=== 类适配器模式 ====
电压 =220 伏
电压为 5V, 能够充电~~
类适配器模式注意事项和细节
Java 是单继承机制
,所以类适配器须要继承 src 类这一点算是一个毛病, 因为这要求 dst 必须是接口, 有肯定局限性
;
src 类的办法在 Adapter 中 都会裸露进去
,也减少了应用的老本。
然而其继承了 src 类,所以它可 以依据需要重写 src 类的办法,使得 Adapter 的灵活性加强
了。
四、对象适配器
在之前解说的类适配器的时候,发现 须要继承 src 类,增加了耦合度
而对象适配器和类适配器模式雷同,只是将 Adapter 类作进行批改
咱们将 不是继承 src 类,而是持有 src 类的实例
,以解决兼容性的问题。
即:持有 src 类,实现 dst 类接口,实现 src->dst 的适配
依据“合成复用准则”,在零碎中尽量应用关联关系(聚合)来代替继承关系
依据对象适配器的思路,咱们的类图是这样的状况
依据咱们的思路,原先输入 5v 的接口不动,还是原来的样子
// 适配接口
interface IVoltage5V {public int output5V();
}
并且原先一个 220v 的电源类输入电压也是无需扭转的样子
// 被适配的类
class Voltage220V {
// 输入 220V 的电压
public int output220V() {
int src = 220;
System.out.println("电压 =" + src + "伏");
return src;
}
}
接下来咱们批改原先的适配器类,增加结构器传入 220v 输入类
// 适配器类
class VoltageAdapter implements IVoltage5V {
private Voltage220V voltage220V;
public VoltageAdapter(Voltage220V voltage220V) {this.voltage220V = voltage220V;}
@Override
public int output5V() {
int dstV = 0;
if(null != voltage220V){
// 获取到 220V 电压
int srcV = voltage220V.output220V();
dstV = srcV / 44 ; // 降压解决转成 5v
System.out.println("适配实现,输入的电压为:" +dstV);
}
return dstV;
}
}
接下里咱们就能够让手机通过适配器,进行充电啦
class Phone {
// 充电
public void charging(IVoltage5V iVoltage5V) {if (iVoltage5V.output5V() == 5) {System.out.println("电压为 5V, 能够充电~~");
} else if (iVoltage5V.output5V() > 5) {System.out.println("电压大于 5V, 不能充电~~");
}
}
}
接下来让咱们看看这样 demo 是怎么进行的呢?
public static void main(String[] args) {System.out.println("=== 类适配器模式 ====");
Phone phone = new Phone();
phone.charging(new VoltageAdapter(new Voltage220V()));
}
运行后果如下:=== 类适配器模式 ====
电压 =220 伏
适配实现,输入的电压为:5
电压为 5V, 能够充电~~
对象适配器模式注意事项和细节
对象适配器和类适配器其实算是 同一种思维,只不过实现形式不同
。
依据 合成复用准则,应用组合代替继承
,所以它 解决了类适配器必须继承 src 的局限性问题
,也不再要求 dst 必须是接口。
应用 老本更低,更灵便
五、接口适配器模式
一些书籍称为:缺省适配器模式(有选择性)
外围思路:当不须要全副实现接口提供的办法时,可先设计一个抽象类实现接口
,并为 该接口中每个办法提供一个默认实现(空办法)
,那么该抽象类的子类可有选择地笼罩父类的某些办法来实现需求
实用于 一个接口不想应用其所有的办法
的状况
利用实例阐明
在 Android 中的 属性动画 ValueAnimator 类能够通过 addListener(AnimatorListener listener)办法增加监听器
个别写的写法如下:
ValueAnimator valueAnimator = ValueAnimator.ofInt(0,100);
valueAnimator.addListener(new Animator.AnimatorListenerO{
// 默认实现(空办法)@Overmride
public void onAnimationStant(Animator animation){}
// 默认实现(空办法)@Ovemide
public void onAnimationEnd(Animator animation){}
// 默认实现(空办法)@Overide
public void onAnimationCancel(Animator animation){}
// 默认实现(空办法)@Ovemide
public woid onAnimationRepeat(Animator animation){}});
有时候咱们不想实现 Animator.AnimatorListener 接口的全副办法,咱们只想监听 onAnimationStart,咱们会如下写
ValueAnimator valueAnimator = ValueAnimator.ofInt(0,100);
valueAnimator.addListener(new Animator.AnimatorListenerO{
// 默认实现(空办法)@Overmride
public void onAnimationStant(Animator animation){}});
诶,为什么咱们能够这样,起因在于有一个抽象类默认实现了空办法
首先是咱们的接口:AnimatorListener
public static interface AnimatorListener {void onAnimationStart(Animator animation);
void onAnimationEnd(Animator animation);
void onAnimationCancel(Animator animation);
void onAnimationRepeat(Animator animation);
}
接下来使咱们的抽象类:AnimatorListenerAdapter
public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener,
Animator.AnimatorPauseListener {
// 默认实现(空办法)@Overmride
public void onAnimationStant(Animator animation){}
// 默认实现(空办法)@Ovemide
public void onAnimationEnd(Animator animation){}
// 默认实现(空办法)@Overide
public void onAnimationCancel(Animator animation){}
// 默认实现(空办法)@Ovemide
public woid onAnimationRepeat(Animator animation){}}
也就是说做了一个抽象类,实现了两个接口,并且以默认实现空办法
这就是为什么咱们能够只写 start 办法也能够,重写本人关怀的办法
那么咱们的类图,其实就是这样的,可如下图所示
接下来咱们应用 demo 领会领会,首先先创一个接口
interface Interface4 {public void m1();
public void m2();
public void m3();
public void m4();}
同时咱们依据思路创立 Adapter 抽象类继承这个接口,默认实现空办法
// 在 AbsAdapter 咱们将 Interface4 的办法进行默认实现
abstract class AbsAdapter implements Interface4 {
// 默认实现
@Override
public void m1() {}
@Override
public void m2() {}
@Override
public void m3() {}
@Override
public void m4() {}
}
当咱们只须要实现 m1 的时候,能够看看 demo 是怎么做的?
public static void main(String[] args) {AbsAdapter absadapter = new AbsAdapter() {
@Override
public void m1() {System.out.println("应用 m1 的办法");
}
};
absadapter.m1();}
运行后果如下:应用 m1 的办法
这里咱们就采纳了匿名外部类的形式去实现 m1 即可,无需全副实现
匿名外部类的定义
先看匿名外部类的定义的语法格局:
new 实现接口(){// 匿名外部类类体局部}
new 父类结构器(实参列表){// 匿名外部类类体局部}
这两种形式的定义别离对应两种形式,一种是接口,另一种是抽象类。
对于实现接口,因为 接口是没有构造函数的
,留神这里肯定是空参数。
第二种是调用 父类的结构器,留神此处能够是空参数,也能够传入参数
参考资料
尚硅谷:设计模式(韩顺平老师):适配器模式
Refactoring.Guru:《深刻设计模式》