2

finalその値を変更する唯一の方法が新しいインスタンスを作成することであることを確認するために、不変データ モデル クラスをマークしました。(残念ながら、フィールドは Hibernate によって入力される必要があるため、final にすることはできません。)

モデルの無効なインスタンスで呼び出されたときに別のクラスが正しい例外をスローすることを確認するまで、これはうまくいきました。モデルのコンストラクターは引数を検証するため、リフレクションを使用してフィールドを設定する必要があります。モデルにはかなりの数のフィールドがあり、フィールド名をハードコーディングする必要があるため、これは非常に扱いにくいものです。

最終的なモデルであるため、モデルをモックすることもできません。(クラスを不変に保ちながらモックを有効にするためにインターフェイスを使用する必要があるかどうかも議論の余地があります。インターフェイスを持つことにより、インスタンスの存続期間を通じてメソッドが同じ値を返さなければならないことをプログラムで強制する方法はありません。)

この場合、人々は通常何をしますか?これに対する標準的なアプローチはありますか?

4

2 に答える 2

3

一般的に言えば、データ オブジェクトをモックする必要はありません。データ オブジェクトにはロジックや外部依存関係がないようにする必要があるため、オブジェクトをモックすることはほとんどありません。代わりに、必要に応じてメソッドに入力できる偽のインスタンスを非常に簡単に作成できるようにします。

さらに、Hibernate で永続化されたオブジェクトを不変として扱うのを避けたい理由が他にもいくつかあります。

  • Hibernate が提供するオブジェクトは本質的にスレッドセーフではないため、不変値オブジェクトが通常提供するスレッドセーフの利点を失います。
  • オブジェクトが実際にはプロキシであり、おそらくfinalセマンティクスを弱めていることに気付くかもしれません。
  • Hibernate で制御されたオブジェクトは、セッションがまだ開いているかどうか(アタッチされているかデタッチされているか) でまったく異なる動作をするため、不変オブジェクトの選択としては非常に不適切です。不変オブジェクトがセッションの有効期間に依存している場合、実際には不変ではありません。
  • 一部のオブジェクトは、データベース層の検証を超えて、アプリケーション層で有効または無効になる可能性があるようです。これにより、検証に関する懸念をカプセル化することが少し難しくなります。
  • これは、不変値オブジェクトに典型的なインスタンス コントロールの種類とは対照的です。
  • オブジェクトは本質的に変更可能であるため、equals と hashCode をオーバーライドするのはより複雑です。

私のアドバイス?Hibernate DAO が付与できるよりも多くの不変性とデータ検証の保証が必要な場合は、最終フィールド (またはプライベート コンストラクターと静的ファクトリ メソッド) を持つ真の最終不変クラスを作成し、コンストラクタ (または静的ファクトリ メソッド) を作成します。 Hibernate DAO から値をコピーします。

このオプションを決定した場合、大まかに並行して変更される2 つのデータ オブジェクトのオーバーヘッドに悩まされますが、(Hibernate オブジェクトが発散する場合に備えて) 懸念を分離する利点と、真に不変なオブジェクトの容易さも得られます。テスト用に簡単に作成できる、equals-and-hashcode-overriding、セッションに依存しない、有効性が保証されたオブジェクト。

于 2012-11-05T02:30:55.197 に答える
0

わかりやすくするために、クラスを final にすると、サブクラス化されなくなります。これは、クラスをさらに洗練する必要がない場合に適しています。

クラス レベルの変数を final としてマークするということは、一度だけ割り当てられることを意味します。プリミティブと文字列のような不変オブジェクトの場合、これには変数が不変になるという副作用があります (デフォルトで)。

ただし、Date のような変更可能なオブジェクトの場合、変数は常に同じインスタンスを参照しますが、そのインスタンスにアクセスできる他のユーザーは、その状態を変更することができます。たとえば、メソッドがあった場合

public Date getCreatedDate(){
     return this.created; // class variable declared as private final Date created...;
}

その後、任意の呼び出し元が作成されたインスタンスにアクセスし、その状態を変更できます。真に不変の値のみを返すか、クローンを返す方がよいでしょう。

public Date getCreatedDate(){
     return this.created.clone();
}

編集

「値を変更する唯一の方法が新しいインスタンスを作成することであることを確認するために、不変データ モデル クラスを final としてマークしました。」

私が理解している問題は、クラス A がクラス B に依存していることです。クラス A をテストしたいのですが、クラス B を最終としてマークしたため、クラス B をモックできません。クラス B を final としてマークして、不変にしました (内部状態が変更されないようにします)。クラスを final とマークすると、サブクラス化できなくなるため、これは正しくありません。インスタンスの内部状態を変更する機能とは関係ありません。

final を使用しても、望ましい効果はありません。フィールドを最終としてマークすることはオプションではなく、上記の理由でクラスを不変にすることはありません。データを保護する唯一の方法は、データのクライアントが、データの内部状態を構成するオブジェクトにアクセスできないようにすることです。

あなたが唯一の開発者ではないと仮定すると、意図しない更新からデータのユーザーを保護する必要があります。getter から確実にクローンを返すことは、1 つの方法です。チーム メンバーがサブクラスを作成してデータを変更するのは、プログラミングが悪いだけであり、意図的ではなく、ポリシーとコードのレビューによって管理できます。

未知の開発者による外部干渉からコードを保護したい場合 (たとえば、同じ名前空間を使用してコードを挿入するコードを作成する場合)、パッケージ シーリングなどの他のアプローチを利用できます。

于 2012-11-05T02:34:16.060 に答える