文章目录
十四、适配器设计模式
14.1 适配器设计模式简介
14.1.1 适配器设计模式概述
适配器设计模式(Adapter Pattern):将一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口不匹配而无法在一起工作的两个类能够在一起工作。适配器模式起着一个转换的作用,将一种接口转化为另一种符合需求的接口;
适配器模式在生活中有很多应用场景,例如:电源转接头、手机充电转换头、变压器、扩展坞、以及各种外接设备的转换器。这些场景都是在输入一个不是目标的功能接口但结果却转换为目标接口;
例如需要USB转换TypeC,那么首先需要有USB转换为TypeC的转换器(适配器),将USB接口插入到转换器中,然后转换器输出TypeC接口;
14.1.2 适配器设计模式的角色
在适配器设计模式中,有如下几种角色:
- 1)目标角色(ITarget):当前系统业务所期待的接口(需要转换为什么接口),例如USB转TypeC时,最终需要转换为TyepC,TypeC就是目标角色;
- 2)源角色(Adaptee):满足客户需求的功能(需要适配器转换才满足),但接口不匹配的接口实例。USB转TypeC案例中的USB接口;
- 3)适配器(Adapter):将源角色(Apdatee)转换为目标角色(ITarget)的适配器实例。USB转TypeC案例中的那个转接器;
14.2 适配器设计模式的实现
适配器设计模式的实现有三种:类适配器、对象适配器、接口适配器。
14.2.1 类适配器
类适配器的原理就是让适配器(Adapter)来继承源角色(Adaptee)来实现适配器的功能;
【案例】
小灰买了台电风扇,是两脚插座的,但是家里电视旁边的插座只剩一个三角插座了,所以小灰买了个二脚转三脚插座转换器,只需要插入二脚插头再使用转换器的三脚插头就可以插进三脚插座了;
UML类图:
- 两脚插头(源角色)
package com.pattern.demo01_类适配器;
/**
* @author lscl
* @version 1.0
* @intro: 两脚插头(源角色)
*/
public class TwoPinPlug {
private int plugCount = 2;
public int use() {
System.out.println("使用【" + plugCount + "】脚插头");
return plugCount;
}
}
- 三脚插头(目标角色):需要将两脚插头转化为三脚插头
package com.pattern.demo01_类适配器;
/**
* @author lscl
* @version 1.0
* @intro: 三角插头(目标角色)
*/
public interface ThreePinPlug {
int use();
}
- 插头转换器(适配器)
package com.pattern.demo01_类适配器;
/**
* @author lscl
* @version 1.0
* @intro: 插头转换器(适配器)
*/
public class PinPlugAdapter extends TwoPinPlug implements ThreePinPlug {
@Override
public int use() {
// 使用二脚插头的基本功能
int plugCount = super.use();
// 在二脚插头的基础上升级(适配)
int adapterCount = plugCount + 1;
System.out.println("适配为:使用Adapter输入【" + plugCount + "】脚插,输出【" + adapterCount + "】脚插");
return adapterCount;
}
}
- 测试代码:
package com.pattern.demo01_类适配器;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01 {
public static void main(String[] args) {
// 使用转换器转换为三脚插头
ThreePinPlug plug=new PinPlugAdapter();
// 使用三脚插头
plug.use();
}
}
14.2.2 对象适配器
对象适配器的原理就是让适配器(Adapter)通过聚合/组合源角色(Adaptee)的方式来实现适配器的功能;
UML类图:
- 修改适配器代码:
package com.pattern.demo02_对象适配器;
import com.pattern.demo01_类适配器.ThreePinPlug;
import com.pattern.demo01_类适配器.TwoPinPlug;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class PinPlugAdapter implements ThreePinPlug {
private TwoPinPlug twoPinPlug;
// 通过聚合/组合的方式来获取源对象功能
public PinPlugAdapter(TwoPinPlug twoPinPlug){
this.twoPinPlug=twoPinPlug;
}
@Override
public int use() {
int plugCount = twoPinPlug.use();
int adapterCount = plugCount + 1;
System.out.println("适配为:使用Adapter输入【" + plugCount + "】脚插,输出【" + adapterCount + "】脚插");
return adapterCount;
}
}
- 测试类:
package com.pattern.demo02_对象适配器;
import com.pattern.demo01_类适配器.ThreePinPlug;
import com.pattern.demo01_类适配器.TwoPinPlug;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01 {
public static void main(String[] args) {
// 传递两脚插头,使用适配器返回三脚插头
ThreePinPlug plug=new PinPlugAdapter(new TwoPinPlug());
plug.use();
}
}
14.2.3 接口适配器
接口适配器的通用写法有两种,不同的写法应对不同的场景;
1)写法一
【案例】
多功能转接口案例,输入USB、HDIM、RJ45、耳麦等接口都可以转换为TypeC接口,然后插入电脑识别使用;
- 目标角色:
package com.pattern.demo03_接口适配器;
/**
* @author lscl
* @version 1.0
* @intro: 多功能转接器(目标角色)
*/
public interface InterfaceAdapter {
String useUSB();
String useRJ45();
String useTypeC();
String useHDMI();
}
- 源角色:
package com.pattern.demo03_接口适配器;
/**
* @author lscl
* @version 1.0
* @intro: USB接口(源角色)
*/
public class USB {
private String name="USB";
public String use() {
return name;
}
}
- 适配器:
package com.pattern.demo03_接口适配器;
/**
* @author lscl
* @version 1.0
* @intro: TypeC转接器(适配器)
*/
public class TypeCAdapter implements InterfaceAdapter {
private String name="TypeC";
private USB usb;
public TypeCAdapter(USB usb) {
this.usb = usb;
}
@Override
public String useUSB() {
String name = usb.use();
System.out.println("接入【" + name + "】,输出【" + this.name + "】");
return this.name;
}
@Override
public String useRJ45() {
return null;
}
@Override
public String useTypeC() {
return null;
}
@Override
public String useHDMI() {
return null;
}
}
- 测试代码:
package com.pattern.demo03_接口适配器;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01 {
public static void main(String[] args) {
// 插入USB转换为TypeC
InterfaceAdapter adapter=new TypeCAdapter(new USB());
adapter.useUSB();
}
}
2)写法二
接口适配器的第二种写法和我们之前的写法、用法以及功能都不太相关,接口适配器的第二种写法主要的用途是当一个类实现一个接口时,如果只想获取某些功能,由于语法的限制,必须要实现该接口的所有抽象方法,即使重写内容为空。接口适配器则是我们提前编写一个适配器,由适配器来重写所有方法,然后源角色继承适配器来选择性的重写某些方法即可;
JDK1.8推出的接口默认方法就是来解决这个问题的;
【案例】
电子设备功能很多,不同的电子设备具备不同的功能;MP3可以听音乐,MP4可以听音乐、看视频,相机可以拍照,手机可以听音乐、看视频、打电话、拍照等;
- 定义目标角色:
package com.pattern.demo04_接口适配器2;
/**
* @author lscl
* @version 1.0
* @intro: 目标角色
*/
interface DianZiSheBei{
//听音乐
void music();
//打电话
void TV();
//发短信
void sendMsg();
//拍照
void takePhoto();
}
- 适配器:
package com.pattern.demo04_接口适配器2;
/**
* @author lscl
* @version 1.0
* @intro: 适配器
*/
class CameraAdapter implements DianZiSheBei{
@Override
public void music() {
}
@Override
public void TV() {
}
@Override
public void sendMsg() {
}
@Override
public void takePhoto() {
}
}
- 源角色:
package com.pattern.demo04_接口适配器2;
/**
* @author lscl
* @version 1.0
* @intro: 源角色
*/
public class Camera extends CameraAdapter {
@Override
public void takePhoto() {
System.out.println("拍了一张6000W像素的照片!");
}
}
- 测试代码:
package com.pattern.demo04_接口适配器2;
/**
* @author lscl
* @version 1.0
* @intro:
*/
public class Demo01 {
public static void main(String[] args) {
DianZiSheBei dianZiSheBei=new Camera();
dianZiSheBei.takePhoto();
}
}
在上述案例中,DianZiSheBei接口其实违反了单一职责原则,其实应该将听音乐、看视频、打电话、发短信等定义为单独的接口,但这样会造成类的数量变多;再者适配器设计模式本就是对已有的类进行适配,而不是作用于这个类的设计时段;
14.3 适配器设计模式的优缺点
- 优点:
- 1)适配器模式提高了类的复用,具备很高的灵活性,只需要引入适配器而不需改动源代码
- 2)遵循依赖开闭原则、里氏替换原则
- 缺点:
- 1)造成类的数量增多
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/131711.html