Java 中的转换器模式:简化跨层数据转换
约 3 分钟
也称为
- 映射器
- 翻译器
转换器设计模式的意图
转换器模式的目的是提供一种通用的、系统化的方式,用于在对应的数据类型之间进行双向转换。这允许实现干净的、解耦的实现,其中类型彼此之间不知情。此外,转换器模式支持双向集合映射,最大限度地减少了样板代码。
转换器模式的详细说明及现实世界示例
现实世界示例
在现实世界的场景中,考虑一个与第三方图书数据库交互的图书馆系统。该图书馆使用内部图书格式,而第三方数据库使用不同的格式。通过使用转换器模式,一个转换器类可以将第三方图书数据转换为图书馆的格式,反之亦然。这确保了无缝集成,而无需更改任何一个系统的内部结构。
通俗地说
转换器模式简化了将一个类的实例映射到另一个类的实例的过程,确保一致且干净的数据转换。
Java 中转换器模式的编程示例
在应用程序中,数据库层通常包含需要映射到 DTO(数据传输对象)的实体,以供业务逻辑使用。这种映射通常涉及许多类,需要一个通用的解决方案。
我们引入一个通用的Converter
类
public class Converter<T, U> {
private final Function<T, U> fromDto;
private final Function<U, T> fromEntity;
public Converter(final Function<T, U> fromDto, final Function<U, T> fromEntity) {
this.fromDto = fromDto;
this.fromEntity = fromEntity;
}
public final U convertFromDto(final T dto) {
return fromDto.apply(dto);
}
public final T convertFromEntity(final U entity) {
return fromEntity.apply(entity);
}
public final List<U> createFromDtos(final Collection<T> dtos) {
return dtos.stream().map(this::convertFromDto).collect(Collectors.toList());
}
public final List<T> createFromEntities(final Collection<U> entities) {
return entities.stream().map(this::convertFromEntity).collect(Collectors.toList());
}
}
专门的转换器继承自这个基类
public class UserConverter extends Converter<UserDto, User> {
public UserConverter() {
super(UserConverter::convertToEntity, UserConverter::convertToDto);
}
private static UserDto convertToDto(User user) {
return new UserDto(user.firstName(), user.lastName(), user.active(), user.userId());
}
private static User convertToEntity(UserDto dto) {
return new User(dto.firstName(), dto.lastName(), dto.active(), dto.email());
}
}
User
和UserDto
之间的映射变得简单直观
public static void main(String[] args) {
Converter<UserDto, User> userConverter = new UserConverter();
UserDto dtoUser = new UserDto("John", "Doe", true, "whatever[at]wherever.com");
User user = userConverter.convertFromDto(dtoUser);
LOGGER.info("Entity converted from DTO: {}", user);
var users = List.of(
new User("Camile", "Tough", false, "124sad"),
new User("Marti", "Luther", true, "42309fd"),
new User("Kate", "Smith", true, "if0243")
);
LOGGER.info("Domain entities:");
users.stream().map(User::toString).forEach(LOGGER::info);
LOGGER.info("DTO entities converted from domain:");
List<UserDto> dtoEntities = userConverter.createFromEntities(users);
dtoEntities.stream().map(UserDto::toString).forEach(LOGGER::info);
}
程序输出
08:28:27.019 [main] INFO com.iluwatar.converter.App -- Entity converted from DTO: User[firstName=John, lastName=Doe, active=true, userId=whatever[at]wherever.com]
08:28:27.035 [main] INFO com.iluwatar.converter.App -- Domain entities:
08:28:27.035 [main] INFO com.iluwatar.converter.App -- User[firstName=Camile, lastName=Tough, active=false, userId=124sad]
08:28:27.035 [main] INFO com.iluwatar.converter.App -- User[firstName=Marti, lastName=Luther, active=true, userId=42309fd]
08:28:27.035 [main] INFO com.iluwatar.converter.App -- User[firstName=Kate, lastName=Smith, active=true, userId=if0243]
08:28:27.036 [main] INFO com.iluwatar.converter.App -- DTO entities converted from domain:
08:28:27.037 [main] INFO com.iluwatar.converter.App -- UserDto[firstName=Camile, lastName=Tough, active=false, email=124sad]
08:28:27.037 [main] INFO com.iluwatar.converter.App -- UserDto[firstName=Marti, lastName=Luther, active=true, email=42309fd]
08:28:27.037 [main] INFO com.iluwatar.converter.App -- UserDto[firstName=Kate, lastName=Smith, active=true, email=if0243]
何时在 Java 中使用转换器模式
在以下情况下使用转换器模式
- 当存在逻辑上相互对应的类型,并且需要在它们之间进行转换时。
- 在与外部系统或服务交互的应用程序中,这些系统或服务需要以特定格式接收数据。
- 用于遗留系统集成,其中数据模型与较新的系统有很大差异。
- 当旨在封装转换逻辑以促进单一职责和更简洁的代码时。
Java 转换器模式教程
Java 中转换器模式的现实世界应用
- 多层应用程序中的数据传输对象 (DTO) 转换。
- 将第三方数据结构或 API 响应改编为内部模型。
- ORM(对象关系映射)框架,用于在数据库记录和领域对象之间进行映射。
- 微服务架构,用于不同服务之间的数据交换。
转换器模式的优缺点
优点
- 关注点分离:将转换逻辑封装在一个组件中,使应用程序的其余部分无需了解转换细节。
- 可重用性:转换器组件可以在整个应用程序中甚至在不同的应用程序中重用。
- 灵活性:易于添加新的转换,而不会影响现有代码,符合开闭原则。
- 互操作性:通过转换数据格式,促进不同系统或应用程序层之间的通信。
权衡
- 开销:引入转换器可能会增加复杂性和潜在的性能开销,尤其是在具有多种数据格式的系统中。
- 重复:如果管理不当,存在模型定义重复的风险,导致维护工作增加。