本期内容为自己总结归档,共分十一章,本人遇到过的面试问题会重点标记。
(若有任何疑问,可在评论区告诉我,看到就回复)
第一部分:适配器模式的核心概念
1.1 什么是适配器模式?
适配器模式是一种结构型设计模式,它通过将一个类的接口转换成客户期望的另一个接口,让那些原本接口不兼容的类能够一起工作。
核心思想:包装一个对象,使其接口与另一个接口兼容,而不改变原始对象的实现。
1.2 适配器模式的三个角色

-
Target(目标接口):客户端期望的接口
-
Adaptee(被适配者):需要被适配的现有接口
-
Adapter(适配器):将Adaptee的接口转换为Target接口
1.3 适配器模式的两种实现方式
方式一:对象适配器(推荐)
通过组合方式实现,更灵活,符合"组合优于继承"原则。
java
// 1. 目标接口:文件阅读器接口
public interface FileReader {
String read(String fileName);
}
// 2. 被适配者:现有的CSV文件读取器
public class CSVReader {
public String readCSV(String csvFile) {
System.out.println("读取CSV文件: " + csvFile);
// 模拟读取CSV文件
return "CSV数据: 姓名,年龄,职业";
}
}
// 3. 适配器:将CSV读取器适配为文件阅读器(通过组合实现)
public class CSVReaderAdapter implements FileReader {
private CSVReader csvReader;
public CSVReaderAdapter(CSVReader csvReader) {
this.csvReader = csvReader;
}
@Override
public String read(String fileName) {
// 验证文件扩展名
if (!fileName.endsWith(".csv")) {
throw new IllegalArgumentException("仅支持CSV文件");
}
System.out.println("适配器:将通用文件读取转换为CSV读取");
// 调用被适配者的方法
return csvReader.readCSV(fileName);
}
}
// 4. 被适配者:现有的JSON文件读取器
public class JSONReader {
public String parseJSON(String jsonFile) {
System.out.println("解析JSON文件: " + jsonFile);
return "JSON数据: {\"name\": \"张三\"}";
}
}
// 5. JSON适配器
public class JSONReaderAdapter implements FileReader {
private JSONReader jsonReader;
public JSONReaderAdapter(JSONReader jsonReader) {
this.jsonReader = jsonReader;
}
@Override
public String read(String fileName) {
if (!fileName.endsWith(".json")) {
throw new IllegalArgumentException("仅支持JSON文件");
}
System.out.println("适配器:将通用文件读取转换为JSON解析");
return jsonReader.parseJSON(fileName);
}
}
// 6. 客户端:统一的文件处理系统
public class FileProcessor {
public void processFile(FileReader reader, String fileName) {
System.out.println("\n开始处理文件: " + fileName);
String content = reader.read(fileName);
System.out.println("文件内容: " + content);
// 进一步处理内容...
}
}
// 7. 测试
public class ObjectAdapterDemo {
public static void main(String[] args) {
FileProcessor processor = new FileProcessor();
// 处理CSV文件
CSVReader csvReader = new CSVReader();
FileReader csvAdapter = new CSVReaderAdapter(csvReader);
processor.processFile(csvAdapter, "data.csv");
// 处理JSON文件
JSONReader jsonReader = new JSONReader();
FileReader jsonAdapter = new JSONReaderAdapter(jsonReader);
processor.processFile(jsonAdapter, "config.json");
// 尝试处理不支持的文件类型
try {
processor.processFile(csvAdapter, "data.txt");
} catch (IllegalArgumentException e) {
System.out.println("错误: " + e.getMessage());
}
}
}
方式二:类适配器
通过多重继承实现(在Java中通过继承和接口实现)。
java
// 1. 目标接口:客户端期望的充电器接口
public interface USBCharger {
void chargeWithUSB();
}
// 2. 被适配者:现有的Type-C充电器
public class TypeCCharger {
public void chargeWithTypeC() {
System.out.println("使用Type-C接口充电...");
}
}
// 3. 适配器:将Type-C转换为USB(通过继承实现)
public class TypeCToUSBAdapter extends TypeCCharger implements USBCharger {
@Override
public void chargeWithUSB() {
System.out.println("适配器:将USB转换为Type-C");
// 调用被适配者的方法
chargeWithTypeC();
}
}
// 4. 客户端:只能使用USB充电的设备
public class USBDevice {
public void charge(USBCharger charger) {
System.out.println("设备开始充电...");
charger.chargeWithUSB();
System.out.println("设备充电完成");
}
}
// 5. 测试
public class ClassAdapterDemo {
public static void main(String[] args) {
USBDevice device = new USBDevice();
// 使用适配器充电
TypeCToUSBAdapter adapter = new TypeCToUSBAdapter();
device.charge(adapter);
}
}
1.4 核心特点
-
接口转换:不改变原有功能,只改变访问方式
-
透明性:客户端不知道适配器的存在
-
复用性:可以让不兼容的类协同工作
-
解耦:分离了客户端与被适配者
第二部分:适配器模式的实现
2.1 基础示例:媒体播放器
假设我们有一个音频播放器,只能播放MP3格式,但需要支持更先进的播放器功能。
java
// 目标接口:音频播放器
public interface MediaPlayer {
void play(String audioType, String fileName);
}
// 被适配者:高级媒体播放器
public class AdvancedMediaPlayer {
public void playVlc(String fileName) {
System.out.println("播放VLC文件: " + fileName);
}
public void playMp4(String fileName) {
System.out.println("播放MP4文件: " + fileName);
}
}
// 适配器:让高级播放器适配基本播放器接口
public class MediaAdapter implements MediaPlayer {
private AdvancedMediaPlayer advancedPlayer;
public MediaAdapter(AdvancedMediaPlayer advancedPlayer) {
this.advancedPlayer = advancedPlayer;
}
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedPlayer.playVlc(fileName);
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedPlayer.playMp4(fileName);
} else {
System.out.println("不支持的文件格式: " + audioType);
}
}
}
// 客户端使用
public class AudioPlayer {
private MediaPlayer player;
public AudioPlayer(MediaPlayer player) {
this.player = player;
}
public void playAudio(String type, String file) {
player.play(type, file);
}
}
2.2 实际应用:支付网关集成
电商系统需要集成多个支付渠道,每个渠道的API都不同。
java
// 统一支付接口
public interface PaymentGateway {
boolean processPayment(double amount, String currency);
}
// PayPal支付(被适配者)
public class PayPalService {
public boolean makePayment(double usdAmount) {
System.out.println("PayPal处理支付: $" + usdAmount);
return true;
}
}
// PayPal适配器
public class PayPalAdapter implements PaymentGateway {
private PayPalService paypalService;
public PayPalAdapter(PayPalService paypalService) {
this.paypalService = paypalService;
}
@Override
public boolean processPayment(double amount, String currency) {
// 转换货币(简单示例)
double usdAmount = convertToUSD(amount, currency);
return paypalService.makePayment(usdAmount);
}
private double convertToUSD(double amount, String currency) {
// 汇率转换逻辑
return amount * 0.15; // 假设转换率
}
}
第三部分:Java和Spring中的适配器模式
3.1 Java标准库中的应用
java
// 1. 字节流到字符流的适配器
InputStream inputStream = new FileInputStream("file.txt");
Reader reader = new InputStreamReader(inputStream, "UTF-8");
// 2. 数组到列表的适配器
String[] array = {"A", "B", "C"};
List<String> list = Arrays.asList(array); // 返回的是适配器列表
// 3. 枚举到迭代器的适配器
Vector<String> vector = new Vector<>();
Enumeration<String> enumeration = vector.elements();
Iterator<String> iterator = Collections.asIterator(enumeration);
3.2 Spring框架中的应用
Spring MVC的HandlerAdapter :
Spring MVC使用适配器模式处理不同类型的控制器,让DispatcherServlet可以统一调用不同的处理器。
java
// Spring中的简化示例
public interface HandlerAdapter {
boolean supports(Object handler);
ModelAndView handle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception;
}
// 使用适配器处理不同类型的控制器
@Controller
public class UserController {
@GetMapping("/user/{id}")
public String getUser(@PathVariable Long id) {
return "user";
}
}
Spring AOP的适配器 :
Spring AOP使用适配器将不同的Advice类型适配到统一的MethodInterceptor接口。
java
// AOP联盟的标准接口
public interface org.aopalliance.intercept.MethodInterceptor {
Object invoke(MethodInvocation invocation) throws Throwable;
}
// Spring的Advice接口
public interface org.springframework.aop.Advice {}
// 不同的Advice类型
public interface MethodBeforeAdvice extends BeforeAdvice {
void before(Method method, Object[] args, Object target) throws Throwable;
}
public interface AfterReturningAdvice extends AfterAdvice {
void afterReturning(Object returnValue, Method method,
Object[] args, Object target) throws Throwable;
}
// 适配器:将Spring Advice适配为AOP联盟的MethodInterceptor
public class MethodBeforeAdviceAdapter implements AdvisorAdapter {
@Override
public boolean supportsAdvice(Advice advice) {
return advice instanceof MethodBeforeAdvice;
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
// 创建适配器,将MethodBeforeAdvice适配为MethodInterceptor
return new MethodBeforeAdviceInterceptor(advice);
}
}
// 具体的适配器实现
public class MethodBeforeAdviceInterceptor implements MethodInterceptor {
private final MethodBeforeAdvice advice;
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
this.advice = advice;
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
// 在执行目标方法前执行advice
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
return mi.proceed();
}
}
总结
4.1 何时使用适配器模式
使用适配器模式的典型场景:
-
系统集成:需要集成遗留系统或第三方库,但接口不兼容
-
接口升级:系统接口升级,但需要保持向后兼容
-
统一接口:需要为多个类似但接口不同的类提供统一接口
-
复用现有类:想使用现有类,但其接口不符合需求
4.2 优缺点
优点
提高代码复用性:可以让原本无法使用的代码得到复用
符合开闭原则:可以在不修改现有代码的情况下引入新功能
提高系统灵活性:可以动态适配不同的实现
解耦客户端与被适配者:客户端只依赖目标接口
保持兼容性:可以在升级接口时保持向后兼容
缺点
增加系统复杂度:引入额外的适配器类
可能降低性能:额外的调用层次可能带来性能开销
可能隐藏设计问题:过度使用可能掩盖接口设计的问题
调试困难:多层适配可能使调试更复杂
4.3 避免适配器模式的误用
适配器模式不应被滥用:
-
不要过早使用适配器:在设计阶段应尽量设计兼容的接口
-
避免过度包装:过多的适配层会增加系统复杂度
-
考虑性能影响:适配器可能带来额外的性能开销
反例:不必要的适配器
java
// 反例:接口本来就很相似,不需要适配器
public interface OldService {
void process(String data);
}
public interface NewService {
void handle(String input);
}
// 不需要适配器,直接修改NewService接口或重构客户端
// 适配器应该用于无法修改的接口
OK 六大核心模式系列到此结束,接下来是三大设计模式类型中的其他设计模式