Java设计模式之适配器模式
Java设计模式之适配器模式(Adapter)
适配器模式是一种结构型设计模式,它允许不兼容的接口之间能够协同工作。就像现实生活中的电源适配器一样,它能让不同标准的电器设备正常工作。
什么是适配器模式
适配器模式就像一个"翻译官",它将一个类的接口转换成客户端期望的另一个接口,使得原本由于接口不兼容而不能一起工作的类可以协同工作。
生活中的例子
想象一下,你有一个只有USB-C接口的笔记本电脑,但你的鼠标是USB接口的。这时你需要一个USB转USB-C的适配器,这个适配器就是适配器模式的现实体现。
适配器模式的结构
适配器模式主要包含三个角色:
- 目标(Target):客户端期望的接口
- 适配者(Adaptee):需要被适配的接口
- 适配器(Adapter):将适配者接口转换为目标接口
适配器模式的实现方式
适配器模式有两种实现方式:类适配器和对象适配器。
1. 类适配器(使用继承)
// 目标接口
public interface Target {
void request();
}
// 适配者类
public class Adaptee {
public void specificRequest() {
System.out.println("适配者的特殊请求");
}
}
// 类适配器
public class ClassAdapter extends Adaptee implements Target {
@Override
public void request() {
specificRequest();
}
}
2. 对象适配器(使用组合)
// 目标接口
public interface Target {
void request();
}
// 适配者类
public class Adaptee {
public void specificRequest() {
System.out.println("适配者的特殊请求");
}
}
// 对象适配器
public class ObjectAdapter implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
@Override
public void request() {
adaptee.specificRequest();
}
}
完整代码示例
让我们通过一个完整的例子来理解适配器模式。假设我们有一个媒体播放器,它只能播放MP3格式的文件,但我们希望它也能播放其他格式的文件。
1. 定义目标接口
// 目标接口
public interface MediaPlayer {
void play(String audioType, String fileName);
}
2. 定义适配者接口和实现
// 适配者接口
public interface AdvancedMediaPlayer {
void playVlc(String fileName);
void playMp4(String fileName);
}
// 适配者实现1
public class VlcPlayer implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
System.out.println("Playing vlc file: " + fileName);
}
@Override
public void playMp4(String fileName) {
// 什么也不做
}
}
// 适配者实现2
public class Mp4Player implements AdvancedMediaPlayer {
@Override
public void playVlc(String fileName) {
// 什么也不做
}
@Override
public void playMp4(String fileName) {
System.out.println("Playing mp4 file: " + fileName);
}
}
3. 创建适配器
// 媒体适配器
public class MediaAdapter implements MediaPlayer {
private AdvancedMediaPlayer advancedMusicPlayer;
public MediaAdapter(String audioType) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer = new VlcPlayer();
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer = new Mp4Player();
}
}
@Override
public void play(String audioType, String fileName) {
if (audioType.equalsIgnoreCase("vlc")) {
advancedMusicPlayer.playVlc(fileName);
} else if (audioType.equalsIgnoreCase("mp4")) {
advancedMusicPlayer.playMp4(fileName);
}
}
}
4. 创建音频播放器实现
// 音频播放器实现
public class AudioPlayer implements MediaPlayer {
private MediaAdapter mediaAdapter;
@Override
public void play(String audioType, String fileName) {
// 内置支持MP3格式
if (audioType.equalsIgnoreCase("mp3")) {
System.out.println("Playing mp3 file: " + fileName);
}
// 使用适配器支持其他格式
else if (audioType.equalsIgnoreCase("vlc") || audioType.equalsIgnoreCase("mp4")) {
mediaAdapter = new MediaAdapter(audioType);
mediaAdapter.play(audioType, fileName);
} else {
System.out.println("Invalid media. " + audioType + " format not supported");
}
}
}
5. 测试代码
public class AdapterPatternDemo {
public static void main(String[] args) {
AudioPlayer audioPlayer = new AudioPlayer();
audioPlayer.play("mp3", "beyond the horizon.mp3");
audioPlayer.play("mp4", "alone.mp4");
audioPlayer.play("vlc", "far far away.vlc");
audioPlayer.play("avi", "mind me.avi");
}
}
输出结果:
Playing mp3 file: beyond the horizon.mp3
Playing mp4 file: alone.mp4
Playing vlc file: far far away.vlc
Invalid media. avi format not supported
适配器模式在Spring Boot中的应用
在Spring Boot 2.7.18中,适配器模式有很多应用场景,下面介绍几个常见的例子。
1. Spring MVC中的HandlerAdapter
Spring MVC使用HandlerAdapter来处理不同类型的控制器。每个控制器类型都有对应的HandlerAdapter,这样DispatcherServlet就可以通过统一的接口调用不同类型的控制器。
@Controller
public class MyController {
@RequestMapping("/hello")
public String hello() {
return "hello";
}
}
// Spring内部使用HandlerAdapter来适配不同类型的控制器
// 我们不需要自己实现,这是Spring内部的实现
2. Spring Boot中的消息转换器(HttpMessageConverter)
HttpMessageConverter将HTTP请求和响应转换为Java对象,这也是适配器模式的应用。
@RestController
@RequestMapping("/api")
public class UserController {
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
// 返回User对象,HttpMessageConverter会将其转换为JSON
return new User(id, "John Doe", "john@example.com");
}
@PostMapping("/users")
public User createUser(@RequestBody User user) {
// HttpMessageConverter将请求体转换为User对象
// 保存用户并返回
return user;
}
}
// User类
public class User {
private Long id;
private String name;
private String email;
// 构造方法、getter和setter
}
3. 自定义适配器示例
假设我们有一个旧的用户服务接口,现在需要适配到新的接口。
// 旧的用户服务接口
public interface LegacyUserService {
String getUserData(String userId);
}
// 旧的用户服务实现
@Service
public class LegacyUserServiceImpl implements LegacyUserService {
@Override
public String getUserData(String userId) {
// 返回JSON字符串格式的用户数据
return "{\"id\":\"" + userId + "\", \"name\":\"Legacy User\"}";
}
}
// 新的用户服务接口
public interface NewUserService {
User getUser(String userId);
}
// 用户类
public class User {
private String id;
private String name;
// 构造方法、getter和setter
}
// 适配器实现
@Service
public class UserServiceAdapter implements NewUserService {
@Autowired
private LegacyUserService legacyUserService;
@Override
public User getUser(String userId) {
String userData = legacyUserService.getUserData(userId);
// 解析JSON字符串并转换为User对象
// 这里简化处理,实际项目中可以使用Jackson等库
User user = new User();
user.setId(userId);
user.setName("Legacy User");
return user;
}
}
// 控制器
@RestController
@RequestMapping("/users")
public class UserController {
@Autowired
private NewUserService userService;
@GetMapping("/{id}")
public User getUser(@PathVariable String id) {
return userService.getUser(id);
}
}
适配器模式的优缺点
优点
- 单一职责原则:可以将接口转换代码从业务逻辑中分离出来
- 开闭原则:可以在不修改现有代码的情况下引入新的适配器
- 提高复用性:使得原本由于接口不兼容而不能一起工作的类可以协同工作
- 灵活性:可以在系统运行时切换不同的实现
缺点
- 增加复杂性:增加了系统中类的数量,使系统变得更加复杂
- 过度使用:如果不需要解决接口兼容问题,不应该使用适配器模式
总结
适配器模式是一种非常实用的设计模式,它可以帮助我们解决接口不兼容的问题。在Spring Boot中,适配器模式被广泛应用,特别是在处理不同类型的控制器、消息转换等方面。
通过适配器模式,我们可以让不兼容的接口协同工作,提高代码的复用性和灵活性。在实际开发中,当我们需要集成第三方库或旧系统时,适配器模式是一个很好的选择。
希望这篇文章能帮助你理解适配器模式,并在实际开发中灵活运用它!
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 zyh
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果