web-dev-qa-db-ja.com

空白/空の値オブジェクトはどのようにインスタンス化/構造化する必要がありますか?

疑問に思っていました...空白の値オブジェクトをインスタンス化するためのベストプラクティスとは何ですか? (Java)

例えば。値オブジェクトクラスFooがあり、空白の場合があります。

このようなメソッドを作成することは、クリーンなソリューションでしょうか? (そしておそらく「空白の」インターフェースか何かを追加します)。

interface Blankable {
    public Foo blankInstance();
    public boolean isBlank();
}

class Foo implements Blankable {
    public Foo blankInstance();
    public boolean isBlank();
}

Foo foo = something();
if (foo.isBlank()) // Tell the user

それとも、このようなもっとシンプルなものが良いでしょうか?

class Foo {
    public static Foo BLANK = /* Some constructor */;
}

Foo foo = something();
if (foo == Foo.BLANK) // Tell the user

私は最初の例に傾いていますが、それが広く受け入れられている手法であるかどうかはまだわかりません。

この状況では回避策のように思えるので、Guava Optional <T>クラスの使用には少し抵抗があります(ただし、まだオープンです)。

3
elimirks

Option<T>などのソリューションは、それほど悪い考えではありません。実際、Haskellでは Maybe としてうまく導入されています(ただし、このソリューションが初めて使用されたかどうかはわかりません)。 たぶんモナドはHaskellプログラムで一般的です。以来、他の言語も同じシステムを採用しています。たとえば、Scalaには Option タイプがあります。

Guavaに依存したくない場合は、Java 8を使用して オプション クラスを導入できます。

ただし、Option/Maybe /…タイプは機能的な環境でより関連性があります。なぜなら、通常は、要素が実際に存在する場合は要素から動作/プロパティを取得し、それ以外の場合は何も取得しないかデフォルトの動作/プロパティを取得するためです。 。したがって、Optionsの一般的な使用法は次のとおりです。

  • Option[A]を取得するために、Option[B]に関数A-> Bを適用します。
  • Optionの実際の内容に基づいてコードを切り替える(たとえば、言語がそれをサポートし、Optionに2つのサブタイプがある場合は、パターンマッチングを使用する) 、またはメソッドのオーバーロードを使用して);
  • または、Optionsをフィルタリングして、(非)存在する要素を表します。

代わりに、抽象クラスMyClassと2つの具象サブクラスがある Null Object Pattern を使用できます。 MyRealClassおよびMyNullClassMyClassのインスタンスを操作しますが、MyRealClassのインスタンスを生成します(要素は実際に存在します)またはMyNullClass(要素が存在しない場合)。 MyNullClassには、デフォルトの動作/プロパティが含まれています。 nullオブジェクトがステートレスである場合(これは一般的なケースです)、それらをキャッシュするか、シングルトンにすることさえできます。

このパターンは[Fowler]で説明されています。

[ファウラー]マーティンファウラー、ケントベック リファクタリング:既存のコードのデザインの改善

2
mgoeminne

値オブジェクトの要点は、同等性はアイデンティティに基づいていないということです。 2つの異なる空白のオブジェクトを作成することもできます。実際、すべての空白オブジェクトが同じオブジェクトか異なるオブジェクトかは、実装の詳細である必要があります。これらの理由から、2番目のアプローチは適切ではありません。 Java 8 APIを見ると、次のプロパティを持つ「値ベースのクラス」という概念があります。

  • 最終的で不変です(ただし、変更可能なオブジェクトへの参照が含まれる場合があります)。
  • equals、hashCode、toStringの実装があり、それらはインスタンスの状態からのみ計算され、IDや他のオブジェクトや変数の状態からは計算されません。
  • インスタンス間の参照の等価性(==)、インスタンスのIDハッシュコード、インスタンスの固有ロックの同期など、IDに依存する操作を使用しないでください。
  • 参照の等価性(==)ではなく、equals()のみに基づいて等しいと見なされます。
  • アクセス可能なコンストラクタはありませんが、代わりに、返されたインスタンスのIDに関してコミットメントを行わないファクトリメソッドを介してインスタンス化されます。
  • 等しい場合は自由に置換可能です。つまり、任意の計算またはメソッド呼び出しで、equals()に従って等しい2つのインスタンスxとyを交換しても、動作に目に見える変化は生じません。

ドキュメントはさらに追加します:

プログラムが値ベースのクラスの等しい値への2つの参照を区別しようとすると、参照の等価性を介して直接的に、または同期、IDハッシュ、シリアル化、またはその他のIDセンシティブメカニズムへのアピールを介して間接的に、プログラムが予期しない結果を生成する可能性があります。値ベースのクラスのインスタンスでこのようなID依存操作を使用すると、予測できない影響が生じる可能性があるため、使用を避けてください。

Javaで==演算子を無効にする方法はないため、警告を表示するのが最善です。

したがって、最初のアプローチは正しいです。ただし、foo1.equals(foo2)の場合、foo1.isBlank()は常にtrueでなければならず、foo1 != foo2であってもfoo2.isBlank()である必要があります。

2
Doval