1.需求入手
1.1不同国家/地区插座适配问题
在不同的国家/地区拥有不同的插座的标准。
比如我国大陆地区使用的插座标准如下:
国内大陆地区的插座标准一般是这样的,电压标准是220V。
英标的插座一般是这样的:
英标插座的标准是如下这样的,电压标准是230V。
所以这就出现问题了,假如一个人从中国大陆地区,想要去英标地区(如英国地区,马来西亚)旅游,自己的设备无法使用该地区标准的插座,那该咋办?
当然是使用适配器啦,使用适配器将自己的充电器适配为英标的接口,就可以安全进行使用啦。
适配器如下:
使用这个适配器就可以将国标插头转换为英标插头在英标插座进行使用。
1.2需求分析
现在的需求就是使用适配器模式,在代码层面实现这个转换。
咱们在这里进行分析的案例是手机需要的充电电压是5V,但是此时咱们新国标的电压要求是220V,此时充电器就充当了一个适配器的角色,进行将新国标的电压转换为手机可以接受的5V的电压。
2.适配器模式
2.1什么是适配器模式
1.适配器模式(Adapter Pattern)将某个类的接口转换成客户端期望的另一个接口表示,主要的目的是兼容性,让原本因为接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)
就是将一个类转换为另一个客户端可以进行使用的类。
2.适配器模式属于结构型设计模式。
3.适配器模式主要分为三类:1.类适配器模式。2.对象适配器模式。3.接口适配器模式。
2.2适配器模式的工作原理
1.适配器模式:将一个类的接口转换为另一种接口,让原本接口不兼容和的类可以兼容。
2.从用户的角度看不到适配者,是解耦的(将适配器中的细节抹去进行解耦)
3.用户调用适配器转换出来的目标接口方法,适配器再调用被适配者的相关接口方法。
4.用户收到反馈结果,感觉只是和目标接口交互。
2.3适配器模式的实现思路
创建适配器类:Adapter类,通过继承src类,实现dst接口,完成src => dst的适配。
3.类适配器模式的应用实例
3.1应用实例介绍
还是使用生活中他充电器的例子进行实现一个适配器模式。
充电器本身就是是一个适配器(Adapter)
220V交流电相当于src(被适配者)
目标5V直流电压相当于dst(适配者)
3.2UML类图
这就是整个UML类图的设计。
VoltageAdapter适配者,进行实现了适配者接口(对外开放接口能力),继承了提供者(方便调用提供者的能力),在内部进行转化,方便调用者(Phone)进行调用使用。
其实通过流程图才能看出来很多细节,理解清楚,定义IVoltage5V的意义是为了让适配者去实现这个接口,拥有提供给外部这个方法的能力(使用接口进行的抽象化处理)
VoltageAdapter适配者具有这个能力之后,适配者在内部去实现具体适配逻辑(其实就是去调用提供者,进行一定的转换之后,将信息返回给调用者)
适配者就是一个黑盒子,调用者无需关注里面是如何适配的,只需要调用自己需要的能力即可。
3.3实现代码
3.3.1提供者代码
提供者进行提供220V电压。
/**
* src -> 提供者
*/
public class Voltage220V {
// 输出220V电压
public int output220V() {
int src = 220;
System.out.println("电压 = " + src + " 伏");
return src;
}
}
3.3.2适配者代码
3.3.2.1适配者接口
适配者接口主要进行规范的,规范出接口(定义了调用者可以直接调用的接口),方便适配者进行实现,进而转换提供者为可以进行调用的代码
/**
* 适配者接口
*/
public interface IVoltage5V {
int output5V();
}
3.3.2.2适配者
适配者实现了适配者接口中定义的抽象方法(为调用者提供调用方法),继承了提供者(方便调用转换提供者),在内部进行转化提供者,返回调用者需要的数据。
// 适配器类
public class VoltageAdapter extends Voltage220V implements IVoltage5V {
@Override
public int output5V() {
// 获取到220V电压
int srcV = output220V();
// 将220V电压进行转换为5V
int dstV = srcV / 44;
return dstV;
}
}
3.3.3调用者代码
调用者代码,主要是进行接收一个实现了相关能力接口的是配置,进行调用这个适配者实现的接口。
/**
* 手机
*/
public class Phone {
public void charging(IVoltage5V iVoltage5V) {
if (iVoltage5V.output5V() == 5) {
System.out.println("电压为5V, 可以充电~~");
} else if (iVoltage5V.output5V() > 5) {
System.out.println("电压大于5V, 手机爆炸!!!");
} else {
System.out.println("欠压, 无法充电!!!");
}
}
}
3.3.4测试
测试代码:
/**
* 客户端
*/
public class Client {
public static void main(String[] args) {
System.out.println("==== 类适配器模式 ====");
Phone phone = new Phone();
phone.charging(new VoltageAdapter());
}
}
测试结果:
3.4类适配器模式注意事项
1.Java是单继承机制,所以类适配模式需要继承src类(提供者类),这是一个很大的缺点,一方面要求了抽象适配者必须是一个接口(不能是抽象类之类的),另一方面由于单继承的缘故,适配者的能力调配是依赖于继承获得的,这样就限制了适配者的能力,只能单适配,不能同时做到获取多个能力实现多适配。
2.提供者的方法(public/protected)都会在适配者(Adapter)中暴露出来,会增加使用的成本。
3.由于其继承了调用者类,所以可以根据需求重写提供者类的方法,使得适配者(Adapter)变得更加灵活。
4.对象适配者模式的应用实例
4.1对象适配者模式的介绍
1.基本思路和类的适配者模式相同,只是将适配者类(Adapter)进行修改,不是继承提供者类,而是持有src类的实例,以解决兼容性的问题。
要点:从继承调用者类 => 持有调用者类的实例化对象。
2.根据"合成复用原则",在系统中尽量使用关联关系来替代继承关系。
要点:继承 => 关联(聚合/组合),实现解耦合。
3.对象适配器模式是适配器模式常用的一种。
4.2使用对象适配者模式进行改进
使用实例化对象将继承关系更改为关联关系(即依赖关系)
4.2.1UML类图的设计
主要是进行更改了类的设计,将提供者直接聚合到适配者中。
设置为聚合的关系,在使用适配者的时候,需要将想要使用的提供者聚合到适配者中。
4.2.2实现代码
主要进行展示发生变动的适配者和测试代码。
适配者代码:
适配者主要是通过构造函数的方式,将提供者注入进去。
// 适配器类
public class VoltageAdapter implements IVoltage5V {
// 使用聚合的方式进行注入进来
private Voltage220V voltage220V;
public VoltageAdapter(Voltage220V voltage220V) {
this.voltage220V = voltage220V;
}
@Override
public int output5V() {
// 获取到220V电压
int srcV = voltage220V.output220V();
// 将220V电压进行转换为5V
int dstV = srcV / 44;
return dstV;
}
}
4.3对象适配器使用的注意事项
1.对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。根据合成复用原则,使用聚合/组合关系替代继承,所以它解决了类适配器必须继承提供者的局限性问题,也不会强制要求抽象适配者是一个接口。
2.使用成本更低,更灵活
要点:建议使用对象适配器,不建议使用类适配器。
5.接口适配器模式
接口适配者模式就是将接口作为src,接口作为提供者进行提供能力。
5.1什么是接口适配器模式
1.一些书籍将接口适配器模式称之为:适配器模式或者缺省适配器模式。
2.当不需要全部实现接口提供的方法时,可以先设计一个抽象类实现接口,并为该接口每个方法提供一个默认实现(空方法),那么抽象类子类可以有选择地覆盖父类的某些方法来实现需求。
3.适用于一个接口不想使用其所有方法的情况。
要点:想要抽象依赖一个接口,但是不想实现里面所有的方法的时候与,可以使用抽象类先去实现接口(空方法),这样子类继承抽象类的时候就不需要实现子类中所有的方法。
5.2接口适配器模式的使用(Android)
5.3实现接口适配器模式
5.3.1UML类图的设计
5.3.2定义被适配者(接口)
这是要被适配的接口,有人想要实现它,但是不想全实现一遍。
/**
* 需要适配化的接口
*/
public interface Interface {
void m1();
void m2();
void m3();
void m4();
}
5.3.3定义适配者(抽象类)
定义的抽象类,其实这个抽象类进行表示的就是一个适配者。
/**
* 适配者(抽象类)
*/
public abstract class AbsAdapter implements Interface {
@Override
public void m1() {
}
@Override
public void m2() {
}
@Override
public void m3() {
}
@Override
public void m4() {
}
}
5.3.4上层使用者(实现类)
上层使用者就是去想要实现接口,但是不想实现接口中所有方法的使用者,他要去继承适配者,按需去重写一些方法即可。
/**
* 上层使用者
*/
public class Adapter extends AbsAdapter {
@Override
public void m1() {
System.out.println("m1被调用了");
}
}
5.3.5测试接口适配者模式
测试代码:
进行测试上层实现者去调用实现的方法
/**
* 测试端
*/
public class Client {
public static void main(String[] args) {
Adapter adapter = new Adapter();
adapter.m1();
adapter.m2();
}
}
测试结果:
6.适配者模式的注意事项和细节
1.三种命名方式,是根据src调用者是以怎样的形式给到Adapter适配者(在Adapter里的形式)来命名的。
2.三大适配器:
类适配器:提供者以类给到适配者,在Adapter适配者里,就是将src提供者当做类,继承。
对象适配器:提供者以对象给到适配者,在Adapter适配者里,就是将src提供者作为一个对象,持有。
接口适配器:提供者以接口给到适配者,在Adapter适配者里,就是将src提供者作为一个接口,实现。
3.Adapter模式最大的作用是什么呢?就是将原本不兼容的接口融合到一起进行工作。
4.实际开发中,实现起来的不拘泥于类/对象/接口适配器的方式,还会有多种多样的实现方式。
要点:设计模式是一种思想不是一种编码技术,不拘泥于具体实现,是一种软件工程架构思想。