编程自学指南:java程序设计开发,Java 适配器模式详解
一、课程信息
学习目标
- 理解适配器模式的核心思想和作用。
- 掌握类适配器、对象适配器和接口适配器三种实现方式。
- 能够识别和运用适配器模式解决实际开发中的兼容性问题。
- 了解适配器模式的优缺点和适用场景。
二、课程导入
生活案例引入
- 电源适配器
- 展示不同国家的电源插头和插座图片,说明在国外使用电器时,由于电源接口不匹配,需要使用电源适配器来转换电压和接口。
- 类比到软件中,不同的类或接口可能因为设计不兼容,需要一个 “适配器” 来使它们能够协同工作。
- 耳机转接头
- 举例手机没有 3.5mm 耳机接口,需要使用转接头将传统耳机适配到手机上。
- 引导学生思考在编程中类似的接口不兼容问题及解决办法。
三、核心概念
定义
适配器模式(Adapter Pattern)是一种结构型设计模式,它将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类能一起工作。
角色分析
- 目标接口(Target):客户端所期望的接口,适配器需要实现这个接口。
- 适配者类(Adaptee):需要被适配的类,它有自己的接口,但与目标接口不兼容。
- 适配器类(Adapter):将适配者类的接口转换为目标接口的类,是适配器模式的核心。
工作原理
通过适配器类,将适配者类的功能包装成符合目标接口的形式,供客户端使用。
四、实现方式
类适配器模式
代码示例
// 目标接口
interface Target {
void request();
}
// 适配者类
class Adaptee {
public void specificRequest() {
System.out.println("适配者的特殊请求");
}
}
// 类适配器
class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
specificRequest();
}
}
// 客户端代码
public class ClassAdapterClient {
public static void main(String[] args) {
Target target = new ClassAdapter();
target.request();
}
}
特点
- 适配器类通过继承适配者类并实现目标接口来完成适配。
- 由于 Java 是单继承,这种方式存在一定的局限性。
对象适配器模式
代码示例
// 目标接口
interface Target {
void request();
}
// 适配者类
class Adaptee {
public void specificRequest() {
System.out.println("适配者的特殊请求");
}
}
// 对象适配器
class ObjectAdapter implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
// 客户端代码
public class ObjectAdapterClient {
public static void main(String[] args) {
Adaptee adaptee = new Adaptee();
Target target = new ObjectAdapter(adaptee);
target.request();
}
}
特点
- 适配器类通过持有适配者类的对象来完成适配。
- 避免了类适配器的单继承限制,更加灵活。
接口适配器模式
代码示例
// 目标接口
interface Target {
void method1();
void method2();
void method3();
}
// 抽象适配器类
abstract class AbstractAdapter implements Target {
@Override
public void method1() {}
@Override
public void method2() {}
@Override
public void method3() {}
}
// 具体实现类
class ConcreteAdapter extends AbstractAdapter {
@Override
public void method1() {
System.out.println("实现 method1");
}
}
// 客户端代码
public class InterfaceAdapterClient {
public static void main(String[] args) {
Target target = new ConcreteAdapter();
target.method1();
}
}
特点
- 当目标接口方法较多,但客户端只需要使用其中一部分时,使用接口适配器可以避免实现所有方法。
五、实际应用案例
案例一:旧系统与新系统的数据对接
- 假设旧系统有一个
OldUserService
类,提供了getUserInfoById
方法获取用户信息。
class OldUserService {
public String getUserInfoById(int id) {
return "旧系统用户信息:ID=" + id;
}
}
- 新系统定义了一个
NewUserService
接口,要求实现fetchUser
方法。
interface NewUserService {
String fetchUser(int id);
}
- 为了让旧系统的功能能在新系统中使用,创建一个适配器类。
class UserAdapter implements NewUserService {
private OldUserService oldUserService;
public UserAdapter(OldUserService oldUserService) {
this.oldUserService = oldUserService;
}
@Override
public String fetchUser(int id) {
return oldUserService.getUserInfoById(id);
}
}
- 客户端代码使用适配器。
public class UserAdapterClient {
public static void main(String[] args) {
OldUserService oldUserService = new OldUserService();
NewUserService newUserService = new UserAdapter(oldUserService);
String userInfo = newUserService.fetchUser(1);
System.out.println(userInfo);
}
}
案例二:不同图形库的适配
- 假设现有一个
Circle
类,它有一个drawCircle
方法。
class Circle {
public void drawCircle() {
System.out.println("绘制圆形");
}
}
- 而新的图形绘制接口
Shape
要求实现draw
方法。
interface Shape {
void draw();
}
- 创建适配器类
CircleAdapter
来适配Circle
类。
class CircleAdapter implements Shape {
private Circle circle;
public CircleAdapter(Circle circle) {
this.circle = circle;
}
@Override
public void draw() {
circle.drawCircle();
}
}
- 客户端使用适配器进行图形绘制。
public class ShapeAdapterClient {
public static void main(String[] args) {
Circle circle = new Circle();
Shape shape = new CircleAdapter(circle);
shape.draw();
}
}
六、优缺点与适用场景
优点
- 提高复用性:可以让原本不兼容的类协同工作,复用已有的类。
- 增强扩展性:在不修改原有代码的基础上,通过适配器实现新的功能。
- 灵活性好:可以根据需要选择不同的适配器实现方式。
缺点
- 增加代码复杂度:引入适配器类会增加系统的代码量和复杂度。
- 过多使用会导致系统混乱:如果适配器使用过多,会使系统变得难以理解和维护。
适用场景
- 系统升级或改造:在旧系统和新系统对接时,使用适配器模式可以避免对旧系统进行大规模修改。
- 使用第三方库:当第三方库的接口与自己的系统不兼容时,可以通过适配器进行适配。
- 统一接口:将多个不同的类统一成一个统一的接口,方便客户端使用。
七、课堂练习
练习一
- 现有一个
MediaPlayer
接口,有play
方法,用于播放音乐。
interface MediaPlayer {
void play(String audioType, String fileName);
}
- 有一个
AdvancedMediaPlayer
接口,包含playVlc
和playMp4
方法。
interface AdvancedMediaPlayer {
void playVlc(String fileName);
void playMp4(String fileName);
}
- 实现
VlcPlayer
和Mp4Player
类实现AdvancedMediaPlayer
接口。
class VlcPlayer implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
System.out.println("播放 VLC 文件:" + fileName);
}
@Override
public void playMp4(String fileName) {}
}
class Mp4Player implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {}
@Override
public void playMp4(String fileName) {
System.out.println("播放 MP4 文件:" + fileName);
}
}
- 要求创建一个适配器类
MediaAdapter
,让MediaPlayer
可以播放vlc
和mp4
格式的文件。
练习二
- 有一个
OldPrinter
类,有printOld
方法。
class OldPrinter {
public void printOld(String content) {
System.out.println("旧打印机打印:" + content);
}
}
- 新的
Printer
接口有print
方法。
interface Printer {
void print(String content);
}
- 创建适配器类,让旧打印机可以适配新的打印接口。
八、课程总结
知识回顾
- 适配器模式的定义和核心思想。
- 类适配器、对象适配器和接口适配器的实现方式。
- 适配器模式的优缺点和适用场景。
口诀总结
“适配模式很奇妙,接口转换不可少。类和对象各有招,接口适配更高效。复用扩展真可靠,系统兼容没烦恼。”
九、课后作业
作业一
- 实现一个文件格式转换的适配器模式。假设有一个
OldFileReader
类可以读取.txt
文件,现在需要创建一个适配器,让它可以适配新的FileReader
接口,该接口可以读取.txt
、.csv
和.json
格式的文件。
作业二
- 分析 JDK 中哪些地方使用了适配器模式,并查阅资料进行验证。