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(オブジェクトリレーショナルマッピング)フレームワーク。
- 異なるサービス間のデータ交換のためのマイクロサービスアーキテクチャ。
コンバーターパターンの利点とトレードオフ
利点
- 懸念事項の分離:変換ロジックを単一コンポーネントにカプセル化し、アプリケーションの残りの部分は変換の詳細を認識しません。
- 再利用性:コンバーターコンポーネントは、アプリケーション全体、または異なるアプリケーションで再利用できます。
- 柔軟性:既存のコードに影響を与えることなく新しい変換を簡単に追加でき、オープン/クローズドの原則に準拠します。
- 相互運用性:データ形式を変換することにより、異なるシステムまたはアプリケーションレイヤー間の通信を容易にします。
トレードオフ
- オーバーヘッド:コンバーターを導入すると、特に多数のデータ形式があるシステムでは、複雑さと潜在的なパフォーマンスのオーバーヘッドが追加される可能性があります。
- 重複:注意深く管理しないと、モデル定義が重複するリスクがあり、メンテナンスが増加します。
関連するJavaデザインパターン
- アダプター:インターフェースを適応させるという点で類似していますが、コンバーターはデータモデルに焦点を当てています。
- ファサード:複雑なシステムに対する簡素化されたインターフェースを提供し、データ変換が含まれる場合があります。
- ストラテジー:特に複数の形式が関与する場合、コンバーターは変換に異なるストラテジーを使用できます。