适配器模式(Adapter Pattern)是一种结构型设计模式,它允许不兼容的接口之间可以一起工作。适配器模式通常用于将一个类的接口转换成客户端期望的另一种接口,从而使原本因接口不兼容而不能一起工作的类可以一起工作。
适配器模式的介绍
适配器模式主要涉及三个角色:
-
目标(Target)接口:当前系统业务所期待的接口,它可以是抽象类或接口。
-
待适配的类(Adaptee):需要被适配的类或接口。
-
适配器(Adapter):通过包装一个需要被适配的对象,把原接口转换成目标接口。
适配器模式有两种实现方式:
-
类适配器模式:通过继承来实现适配器功能。
-
对象适配器模式:通过组合来实现适配器功能。
优缺点
优点:
-
提高了类的复用性:系统的需求变化时,不需要修改原有的代码,只需要添加适配器即可重用现有的功能。
-
增加了类的透明性和灵活性:客户端代码可以透明地调用目标接口。
-
灵活性和扩展性都非常好:如果需要改变适配逻辑,只需更换具体的适配器即可。
缺点:
-
过多地使用适配器会使系统凌乱。例如,明明看到调用的是A接口,实际上内部被适配成了B接口的实现。
-
由于Java至多继承一个类,所以至多只能适配一个适配者类,而且目标类必须是接口。
在Spring Cloud中通过Feign使用适配器模式
在Spring Cloud中,Feign是一个声明式的Web服务客户端,它使得编写Web服务客户端变得更加简单。我们可以通过Feign来调用外部接口。当外部接口的参数与我们的系统不兼容时,可以使用适配器模式来进行适配。
假设我们有一个外部微服务,它提供了一个接口,用于获取用户信息,但是这个接口的用户信息对象的结构与我们的系统中使用的用户信息对象的结构不一致。
外部服务的用户信息对象(Adaptee)
public class ExternalUser {
private String firstName;
private String lastName;
private String email;
// getters and setters
}
我们系统中的用户信息对象(Target)
public class InternalUser {
private String fullName;
private String emailAddress;
// getters and setters
}
Feign客户端定义(Adaptee接口)
@FeignClient(name = "external-user-service")
public interface ExternalUserService {
@GetMapping("/user/{id}")
ExternalUser getUserById(@PathVariable("id") String userId);
}
适配器(Adapter)
我们创建一个适配器,它实现我们系统中定义的接口,并在内部调用Feign客户端,将ExternalUser
转换为InternalUser
。
public class UserAdapter implements UserService {
private final ExternalUserService externalUserService;
public UserAdapter(ExternalUserService externalUserService) {
this.externalUserService = externalUserService;
}
@Override
public InternalUser getUserById(String userId) {
ExternalUser externalUser = externalUserService.getUserById(userId);
return new InternalUser(externalUser.getFirstName() + " " + externalUser.getLastName(),
externalUser.getEmail());
}
}
使用适配器
现在,当我们的系统需要获取用户信息时,我们可以通过UserAdapter
来获取,它会在内部调用外部服务,并将获取到的ExternalUser
对象转换为我们系统中的InternalUser
对象。
public class UserServiceClient {
private final UserService userService;
public UserServiceClient(UserService userService) {
this.userService = userService;
}
public InternalUser getUserById(String userId) {
return userService.getUserById(userId);
}
}
下面是对象之间的关系:
在这个例子中,UserAdapter
充当了适配器的角色,它把外部服务返回的ExternalUser
对象转换为我们系统内部使用的InternalUser
对象。这样,即使外部服务的接口或数据结构发生变化,我们也只需要在UserAdapter
中做出相应的调整,而不需要修改系统内部的其他部分。这样不仅增加了代码的可维护性,也提高了系统的灵活性和扩展性。
总结
通过适配器方式,我们可以将不兼容的接口或数据模型转换为我们的系统可以使用的形式,同时保持系统的整洁和一致性。这样做还有助于隔离系统与外部服务的直接依赖,当外部服务变化时,只需修改适配器逻辑,而不会影响到系统的其他部分。