Javaで多くのパラメーターを持つコンストラクターを管理する


105

一部のプロジェクトでは、チェーンを下っていくときにパラメーターを追加するクラス階層があります。下部では、一部のクラスは最大30個のパラメーターを持つことができ、そのうち28個はスーパーコンストラクターに渡されます。

Guiceのようなものを通じて自動化されたDIを使用することは良いことだと認めますが、いくつかの技術的な理由により、これらの特定のプロジェクトはJavaに制限されています。

タイプごとに引数をアルファベット順に整理する規則は機能しません。タイプがリファクタリングされた場合(引数2に渡した円がシェイプになったため)、突然順序が乱れる可能性があるためです。

この質問は、「それが問題である場合は、設計レベルで間違っている」という特定の批判に関係している可能性がありますが、私は任意の視点を探しています。

回答:


264

Builderデザインパターンが役立つ場合があります。次の例を考えてみましょう

public class StudentBuilder
{
    private String _name;
    private int _age = 14;      // this has a default
    private String _motto = ""; // most students don't have one

    public StudentBuilder() { }

    public Student buildStudent()
    {
        return new Student(_name, _age, _motto);
    }

    public StudentBuilder name(String _name)
    {
        this._name = _name;
        return this;
    }

    public StudentBuilder age(int _age)
    {
        this._age = _age;
        return this;
    }

    public StudentBuilder motto(String _motto)
    {
        this._motto = _motto;
        return this;
    }
}

これにより、次のようなコードを記述できます

Student s1 = new StudentBuilder().name("Eli").buildStudent();
Student s2 = new StudentBuilder()
                 .name("Spicoli")
                 .age(16)
                 .motto("Aloha, Mr Hand")
                 .buildStudent();

必須フィールドを省略すると(おそらく名前は必須です)、Studentコンストラクターに例外をスローさせることができます。また、これらの呼び出しの順序は同じように機能するため、引数の順序を追跡する必要なく、デフォルト/オプションの引数を使用できます。


10
もちろん、静的インポートでは、これらの「ビルダー」をまったく「見る」必要はありません。たとえば、ビルダーを返す静的メソッドname(String name)と生徒を返すStudent(StudentBuilder)を使用できます。したがって、Student(name( "Joe")。age(15).motto( "I've wetselfself"));
oxbow_lakes 2008年

2
@oxbow_lakes:あなたの例では、どのクラスに静的メソッドname(String name)がありますか?
user443854

技術的には、Studentクラスを使用して新しい学生を作成することができます。Studentクラス内にメソッドを追加したところ、問題なく動作しました。これにより、別のビルダークラスを用意する必要がなくなりました。これが望ましいかどうかはわかりません。それを構築するために別の(StudentBuilder)クラスを使用する理由はありますか?
WVrock 2014年

1
@WVrock:実装によって異なります。私の回答で言うように、これを生徒のクラス自体で行うと、クラスが半分初期化された状態になる可能性があります。たとえば、まだ初期化されていない必須フィールドがある場合などです。
Eli Courtwright、2014年

@EliCourtwrightそれは好み/コード設計についてだと思います。コンストラクタに例外をスローさせる代わりに、buildStudent()メソッドに例外をスローさせました。
WVrock 2014年

24

関連するパラメーターをオブジェクト内にカプセル化できますか?

たとえば、パラメータが


MyClass(String house, String street, String town, String postcode, String country, int foo, double bar) {
  super(String house, String street, String town, String postcode, String country);
  this.foo = foo;
  this.bar = bar;

それからあなたは代わりに持つことができます:


MyClass(Address homeAddress, int foo, double bar) {
  super(homeAddress);
  this.foo = foo;
  this.bar = bar;
}



8

まあ、ビルダーパターンの使用は1つのソリューションかもしれません。

しかし、20〜30個のパラメーターに到達すると、パラメーター間に高い関係があると思います。したがって、(提案されているように)それらを論理的に健全なデータオブジェクトにラップすることはおそらく最も理にかなっています。このようにして、データオブジェクトはパラメータ間の制約の有効性をすでに確認できます。

過去のすべてのプロジェクトで、パラメータが多すぎると(28ではなく8になりました)、より適切なデータモデルを作成することでコードを無害化することができました。


4

Java 1.4に制約されているため、DIが必要な場合は、Springが非常に適切なオプションになります。DIは、コンストラクターパラメーターがサービスまたは実行時に変化しないものである場所でのみ役立ちます。

オブジェクトの作成方法に可変オプションが必要なために、これらの異なるコンストラクターがすべてある場合は、Builderパターンの使用を真剣に検討する必要があります。


あなたが言ったように、パラメーターはほとんどがサービスなので、DIは私が必要としているものです。他のいくつかの回答で言及されているビルダーパターンは、まさに私が望んでいたものだと思います。
スティーブアームストロング

4

最善の解決策は、コンストラクターのパラメーターが多すぎないことです。コンストラクタで本当に必要なパラメータのみが、オブジェクトを正しく初期化する必要があるパラメータです。複数のパラメーターを持つコンストラクターを持つことができますが、最小パラメーターのみを持つコンストラクターを持つこともできます。追加のコンストラクターがこの単純なコンストラクターを呼び出し、その後、セッターが他のパラメーターを設定します。このようにして、より多くのパラメーターでチェーンの問題を回避することができますが、便利なコンストラクターもいくつかあります。



1

パラメーターの数を減らして継承階層の深さを減らすためのリファクタリングは、私が考えることができるほとんどすべてのことです。ドキュメントを見ながら、すべての呼び出しを行う必要があります。

あなたができることの1つは、いくつかの論理的にグループ化されたパラメーターを独自の上位レベルのオブジェクトにグループ化することですが、それにはそれ自体に問題があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.