22

たくさんのフィールドを持つJAVAクラスがあります。これらは基本的にコンストラクターフェーズで設定する必要があり、変更しないでください。意味的には、クラスは不変のクラスです。

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

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

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

そのどれも完全に満足のいくものではありません。他にアプローチはありますか?ありがとう。一方通行

4

5 に答える 5

27

パラメーターを作成するために、パラメータークラスとFluentBuilderAPIの組み合わせを使用します。

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は不変であり、パラメーターはオプションであり、インターフェースは流暢です。

于 2012-07-07T22:06:10.953 に答える
19

あなたができる2つのこと:

于 2012-07-07T21:56:12.690 に答える
1

これはやや深刻な提案にすぎませんが、ミケラの答えをタイプセーフに変更することができます。

私たちが持っていると言う:

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);
}

これはすべて次のように使用されます。

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

lulzの場合は、マップに流暢なインターフェイスを与えることもできます。

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コンストラクターの代わりに静的ファクトリを使用して、そのようなことに興味がある場合は、より流暢なスタイルのコードを使用できます。のコンストラクターを呼び出すメソッドを拡張して、本質的にビルダーに変えることAPropertyMapができます。buildA

これらのオブジェクトのいくつかをかなり一般的にすることもできます。APropertyそして、非常に単純な特定のサブクラスAPropertyMapを使用して、機能ビットを実行する汎用基本クラスを持つことができます。A

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

于 2012-07-07T23:14:07.157 に答える
0

興味深いオプションの1つはMap<String,Object>、ユーザーが指定したい値を含む入力としてasを受け取るコンストラクターを作成することです。

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

編集:

ランダムな反対票は完全に要点を見逃していると思います。これが常に最良の選択になるとは限りませんが、いくつかの利点がある便利なテクニックです。

  • 簡潔で、個別のコンストラクター/ビルダークラスを作成する必要がありません。
  • これにより、パラメータセットをプログラムで簡単に構築できます(たとえば、解析されたDSLからオブジェクトを構築している場合)
  • これは、動的言語で機能することが頻繁に使用され、証明されている手法です。あなたはまともなテストを書く必要があります(とにかくそれをするべきです!)
于 2012-07-07T22:00:33.947 に答える
0

多くのフィールドがあるということは、1つのクラスがやりすぎであることを示している可能性があります。

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

于 2012-07-07T23:01:52.543 に答える