2012-07-07 9 views
21

私は多くのフィールドを持つJAVAクラスを持っています。基本的にはコンストラクタフェーズに設定し、決して変更しないでください。意味的にクラスは不変のものです。多くのフィールドがデフォルト値の "immutable class"のJavaコンストラクタ?

public class A{ 
    final int a; 
    final short b; 
    final double e; 
    final String f; 
    final String g; 
    //and more 
} 

問題は通常、これらのフィールドは、デフォルト値を持っているので、私はそれらのすべてを持つコンストラクタを持つに常に負担のユーザーを望んでいないということです。ほとんどの時間、彼らはそれらのカップルを設定する必要があります。これを解決するにはいくつかの方法があります:

  1. 異なる署名を持つコンストラクタがたくさん必要です。
  2. これらのフィールドの集合メソッドを作成し、デフォルト以外の値のみを設定します。しかし、これは何とか不変の性質以外の別のセマンティクスを示しています。
  3. 変更可能な新しいパラメータクラスを作成し、そのクラスをコンストラクタとして使用します。

これはまったく問題ありません。他のアプローチはありますか?ありがとう。あなたが行うことができます

答えて

27

私は、パラメータを作成するためのパラメータクラスの組み合わせと流暢ビルダーAPIを使用します。

public class A { 
    private final int a; 
    private final short b; 
    private final double e; 
    private final String g; 

    public static class Aparam { 
     private int a = 1; 
     private short b = 2; 
     private double e = 3.141593; 
     private String g = "NONE"; 

     public Aparam a(int a) { 
      this.a = a; 
      return this; 
     } 

     public Aparam b(short b) { 
      this.b = b; 
      return this; 
     } 

     public Aparam e(double e) { 
      this.e = e; 
      return this; 
     } 

     public Aparam g(String g) { 
      this.g = g; 
      return this; 
     } 

     public A build() { 
      return new A(this); 
     } 
    } 

    public static Aparam a(int a) { 
     return new Aparam().a(a); 
    } 

    public static Aparam b(short b) { 
     return new Aparam().b(b); 
    } 

    public static Aparam e(double e) { 
     return new Aparam().e(e); 
    } 

    public static Aparam g(String g) { 
     return new Aparam().g(g); 
    } 

    public static A build() { 
     return new Aparam().build(); 
    } 

    private A(Aparam p) { 
     this.a = p.a; 
     this.b = p.b; 
     this.e = p.e; 
     this.g = p.g; 
    } 

    @Override public String toString() { 
     return "{a=" + a + ",b=" + b + ",e=" + e + ",g=" + g + "}"; 
    } 
} 

次に作成しますAのインスタンスは次のようになります。

A a1 = A.build(); 
A a2 = A.a(7).e(17.5).build(); 
A a3 = A.b((short)42).e(2.218282).g("fluent").build(); 

クラスAは不変で、パラメータはオプションであり、インターフェイスは流暢です。

+1

ビルダーにゲッターが必要なく、Aのコンストラクターをプライベートにすることもできます。これにより、コンストラクタの代わりにbuild()メソッドのパラメータをチェックすることもできます。 –

+0

真。私はAのctorを非公開にすることを検討しました。それはおそらくもっときれいです。 –

+0

もう1つの利点は、ビルダーが、パラメータに応じて、A、ABis、またはATer(AのサブクラスであるAbisとAter)のインスタンスを返すことを選択できることです。 –

19

2つのこと 一つの方法:

  • 多くのコンストラクタは
  • が一つの興味深いオプションが含まれている入力としてMap<String,Object>を取るコンストラクタを作成することですbuilder object
+4

+1。 –

+0

私は、ビルダー・パターンが不変クラスのために適切ではないと思います。 – zggame

+0

@zggame:もちろんそうではありません!ビルダーを使用して、別のオブジェクトを作成します。 –

0

を使用してオーバーロードユーザーが指定したい値。

コンストラクタでは、マップに指定されている値がある場合はそれを使用し、存在しない場合はデフォルト値を使用できます。

EDIT:

私はランダムdownvotersが完全にポイントを逃したと思う - これは、常に最良の選択になるだろうされていないが、それはいくつかの利点を有している有用な技術である:

  • これは簡潔で、別のコンストラクタ/ビルダークラスを作成する必要がなくなります
  • パラメータセットを簡単にプログラムで構築できます(たとえば、解析されたDSLからオブジェクトを構築する場合など)
  • これは、頻繁に使用され、動的言語で動作することが証明されている技術です。あなただけの(あなたがとにかくやっすべき!)まともなテストを記述する必要が
+1

これは主にタイプの互換性を持つ多くのエラーにつながります。この場合、ビルダーのアプローチはより良いと言えます(Jordãoの提案通り) –

+0

動的言語のテクニックです。利便性/柔軟性。あなたがアプローチを気に入っているなら、あなたには最高ですが、良いテストを書いてもそれは問題ではないことがわかりました。 – mikera

+0

これはJavaScriptの方法であり、動的タイピングの言語ではうまくいきますが、私はJavaでそれをやりません。すべての引数が同じ型であった場合には動作します。それでも、私はボブ・マーティンが「ハッシュ・マップを渡す」ということは良い考えではないと言っていると思います。-1でも+1でもない –

0

多くのフィールドを持つことは、あるクラスがあまりにも多くのことを示す可能性があります。

多分、いくつかの不変クラスでクラスを分割し、これらのクラスのインスタンスを他のクラスのコンストラクタに渡すことができます。これはコンストラクタの数を制限します。

1

これは半重大な提案ですが、mikera's answerをタイプセーフに変更することができます。

public class A { 
    private final String foo; 
    private final int bar; 
    private final Date baz; 
} 

その後、我々は書く:

public abstract class AProperty<T> { 
    public static final AProperty<String> FOO = new AProperty<String>(String.class) {}; 
    public static final AProperty<Integer> BAR = new AProperty<Integer>(Integer.class) {}; 
    public static final AProperty<Date> BAZ = new AProperty<Date>(Date.class) {}; 

    public final Class<T> propertyClass; 

    private AProperty(Class<T> propertyClass) { 
     this.propertyClass = propertyClass; 
    } 
} 

そして:

public class APropertyMap { 
    private final Map<AProperty<?>, Object> properties = new HashMap<AProperty<?>, Object>(); 

    public <T> void put(AProperty<T> property, T value) { 
     properties.put(property, value); 
    } 
    public <T> T get(AProperty<T> property) { 
     return property.propertyClass.cast(properties.get(property)); 
    } 
} 

先進的なデザインパターンの愛好家および/またはあいまいなJavaのトリックはこれを認識します

は、我々が持っていると言う

タイプセーフな異種コンテナ。ちょうど私が getGenericSuperclass()も使用しなかったことに感謝します。

その後、バックターゲットクラスで:これは、すべてこのように使用されて

public A(APropertyMap properties) { 
    foo = properties.get(AProperty.FOO); 
    bar = properties.get(AProperty.BAR); 
    baz = properties.get(AProperty.BAZ); 
} 

:ちょうどlulzため

APropertyMap properties = new APropertyMap(); 
properties.put(AProperty.FOO, "skidoo"); 
properties.put(AProperty.BAR, 23); 
A a = new A(properties); 

、私たちも、マップに流れるようなインターフェイスを与えることができます

public <T> APropertyMap with(AProperty<T> property, T value) { 
    put(property, value); 
    return this; 
} 

発信者が次のように書くことができます。

A a = new A(new APropertyMap() 
    .with(AProperty.FOO, "skidoo") 
    .with(AProperty.BAR, 23)); 

これにはほとんど改善がありません。 APropertyのタイプは、よりエレガントに処理することができました。 APropertyMapは、コンストラクタの代わりに静的なファクトリを持つことができます。そのような場合は、より流暢なコードを使用できます。 APropertyMapAのコンストラクタを呼び出すメソッドbuildを成長させることができ、本質的にそれをビルダーに変換することができます。

また、これらのオブジェクトの一部をより一般的なものにすることもできます。 APropertyおよびAPropertyMapは、非常に単純なA固有のサブクラスを使用して、機能ビットを行った汎用ベースクラスを持つことができます。

特にエンタープライズを感じていて、ドメインオブジェクトがJPA2エンティティの場合は、メタモデル属性をプロパティオブジェクトとして使用できます。これにより、マップ/ビルダーはもう少し作業をしていますが、まだ簡単です。私は、45行で作業するジェネリックビルダーを持っています。エンティティごとに1つの1行のメソッドを含むサブクラスがあります。

+0

ニース。私は静的なタイピングが大好きです。良い妥協案、私は巨大なMap を持つ別のケースで使うことができるかもしれません。ありがとう。 – zggame

関連する問題