Javaにおける値オブジェクトパターン:不変データ型によるパフォーマンス向上
別名
- 埋め込み値
- イミュータブルオブジェクト
- インライン値
- 統合値
値オブジェクトデザインパターンの意図
Javaの値オブジェクトパターンは、概念的なアイデンティティを持たないドメインの記述的な側面を表す、不変オブジェクトを作成します。 頻繁にアクセスされる不変データを別々に格納するのではなく、使用するオブジェクト内に直接格納することにより、パフォーマンスを向上させ、メモリオーバーヘッドを削減することを目的としています。
現実世界の例を用いた値オブジェクトパターンの詳細な説明
現実世界の例
名刺の場合を考えてみましょう。 この例では、`BusinessCard`クラスは、Javaアプリケーションでの不変データ処理と効率性を実証するために、値オブジェクトとして実装されています。 現実の世界では、名刺には、個人の名前、役職、電話番号、メールアドレスなどの情報が含まれています。 この情報は、個人の連絡先の詳細を説明する具体的かつ完全な属性セットを表していますが、この情報以外にアイデンティティはありません。
ソフトウェアシステムでは、値オブジェクトとして`BusinessCard`クラスを作成できます。 このクラスは不変です。つまり、個人の詳細を含む`BusinessCard`オブジェクトが作成されると、それらの詳細は変更できません。 別の名刺が必要な場合は、既存の名刺を変更するのではなく、新しいインスタンスを作成します。 2つの`BusinessCard`オブジェクトの同等性は、メモリ アドレスではなく、含まれるデータに基づいて判断されるため、同じ詳細を持つ2つの名刺は等しいと見なされます。 これは、現実の世界で名刺が物理的なカード自体ではなく、その内容に基づいて使用および比較される方法を反映しています。
簡単な言葉で
値オブジェクトは、属性の値が同じ場合に等しくなります。
Wikipediaによると
コンピュータサイエンスでは、値オブジェクトとは、等価性がアイデンティティに基づかない単純なエンティティを表す小さなオブジェクトです。つまり、2つの値オブジェクトは、必ずしも同じオブジェクトである必要はなく、同じ値を持つ場合に等しくなります。
Javaにおける値オブジェクトパターンのプログラム例
ロールプレイングゲームには、ヒーローの統計情報を表すクラスがあります。 統計情報には、強さ、知性、運などの属性が含まれています。 異なるヒーローの統計情報は、すべての属性が等しい場合に等しくなければなりません。
値オブジェクトである`HeroStat`クラスを以下に示します。 Lombokの`@Value`アノテーションの使用に注意してください。
@Value(staticConstructor = "valueOf")
@ToString
class HeroStat {
int strength;
int intelligence;
int luck;
}
この例では、3つの異なる`HeroStat`を作成し、それらの同等性を比較します。
public static void main(String[] args) {
var statA = HeroStat.valueOf(10, 5, 0);
var statB = HeroStat.valueOf(10, 5, 0);
var statC = HeroStat.valueOf(5, 1, 8);
LOGGER.info("statA: {}", statA);
LOGGER.info("statB: {}", statB);
LOGGER.info("statC: {}", statC);
LOGGER.info("Are statA and statB equal? {}", statA.equals(statB));
LOGGER.info("Are statA and statC equal? {}", statA.equals(statC));
}
コンソール出力は次のとおりです。
20:11:12.199 [main] INFO com.iluwatar.value.object.App - HeroStat(strength=10, intelligence=5, luck=0)
20:11:12.202 [main] INFO com.iluwatar.value.object.App - HeroStat(strength=10, intelligence=5, luck=0)
20:11:12.202 [main] INFO com.iluwatar.value.object.App - HeroStat(strength=5, intelligence=1, luck=8)
20:11:12.202 [main] INFO com.iluwatar.value.object.App - Is statA and statB equal : true
20:11:12.203 [main] INFO com.iluwatar.value.object.App - Is statA and statC equal : false
Javaで値オブジェクトパターンを使用する場合
以下の場合に値オブジェクトを使用します
- 特に効率的なデータ管理を必要とするシステムにおいて、メモリオーバーヘッドを削減した高パフフォーマンスのJavaアプリケーションが必要な場合に、値オブジェクトパターンを適用します。
- アイデンティティを持たずに、エンティティをまとめて記述する属性のセットを表す場合。
- オブジェクトの同等性がアイデンティティではなく、プロパティの値に基づいている場合。
- オブジェクトを作成後に変更できないようにする必要がある場合。
- アプリケーションが高パフォーマンスを必要とし、関係するデータが不変である場合。
- メモリフットプリントの削減が重要な場合、特にリソースが限られた環境では。
- オブジェクトが特定の不変データに頻繁にアクセスする場合。
値オブジェクトパターン Javaチュートリアル
Javaにおける値オブジェクトパターンの実際のアプリケーション
- 金額、測定値、その他のドメイン固有の値などの複雑なデータ型を実装する場合。
- java.util.Optional
- java.time.LocalDate
- java.awt.Color
- joda-time、money、beans
値オブジェクトパターンの利点とトレードオフ
利点
- オブジェクトを不変にすることでコードを簡素化します。
- オブジェクトの状態は作成後に変更できないため、スレッドセーフです。
- 推論と保守が容易になります。
- 不変データの個別の割り当てを回避することにより、メモリオーバーヘッドを削減します。
- メモリアクセスを最小限に抑え、キャッシュミスを削減することにより、パフォーマンスを向上させます。
トレードオフ
- 変更ごとに新しいオブジェクトを作成すると、複雑なオブジェクトの場合、効率が低下する可能性があります。
- 異なる状態を表す複数のオブジェクトの作成によるメモリ使用量の増加。
- オブジェクト設計の複雑さが増し、システムの密結合につながる可能性があります。
- 埋め込み値を変更するには、この値を埋め込むすべてのオブジェクトに変更を加える必要があるため、保守が複雑になる可能性があります。
関連するJavaデザインパターン
- ファクトリメソッド:値オブジェクトのインスタンスを作成するためによく使用されます。
- フライウェイト:最小限のメモリを使用して大量のオブジェクトをサポートするためにオブジェクトを共有します。意図はいくぶん似ていますが、実装は異なります。
- ビルダー:複雑な値オブジェクトを段階的に構築するために使用できます。
- プロトタイプ:既存の値オブジェクトの複製に使用できますが、不変オブジェクトでは複製はあまり一般的ではありません。
- シングルトン:クラスにインスタンスが1つだけ存在することを保証し、グローバルなアクセスポイントを提供します。共有埋め込み値の管理に使用できます。