1. ホーム
  2. parameters

[解決済み] コンストラクタの引数は何個が多すぎる?[クローズド]

2022-04-27 19:06:02

質問

例えば、Customerというクラスがあり、以下のフィールドを含んでいるとします。

  • ユーザー名
  • 電子メール
  • 名前

また、ビジネスロジックに従って、すべてのCustomerオブジェクトにこれら4つのプロパティが定義されている必要があるとします。

さて、これらのプロパティをそれぞれコンストラクタに指定させることで、かなり簡単にこれを実現することができます。 しかし、Customerオブジェクトにさらに必須フィールドを追加することを強いられると、これがいかに制御不能になるかは一目瞭然です。

コンストラクタに20以上の引数を取り込むクラスを見たことがありますが、使うのが面倒くさいだけです。 しかし、その代わりに、これらのフィールドを必要としない場合、呼び出し側のコードにこれらのプロパティを指定することを依存すると、未定義の情報が発生したり、最悪の場合、オブジェクト参照エラーが発生したりする危険性が出てきます。

それとも、コンストラクタの引数が多すぎて耐えられないかどうかを判断する必要があるのでしょうか?

解決方法は?

考慮すべき2つのデザインアプローチ

その エッセンス パターン

流れるようなインターフェイス パターン

これらは、中間オブジェクトをゆっくり作り上げてから、目的のオブジェクトを一度に作るという点で意図が似ています。

フルエント・インターフェイスの動作例は次の通りです。

public class CustomerBuilder {
    String surname;
    String firstName;
    String ssn;
    public static CustomerBuilder customer() {
        return new CustomerBuilder();
    }
    public CustomerBuilder withSurname(String surname) {
        this.surname = surname; 
        return this; 
    }
    public CustomerBuilder withFirstName(String firstName) {
        this.firstName = firstName;
        return this; 
    }
    public CustomerBuilder withSsn(String ssn) {
        this.ssn = ssn; 
        return this; 
    }
    // client doesn't get to instantiate Customer directly
    public Customer build() {
        return new Customer(this);            
    }
}

public class Customer {
    private final String firstName;
    private final String surname;
    private final String ssn;

    Customer(CustomerBuilder builder) {
        if (builder.firstName == null) throw new NullPointerException("firstName");
        if (builder.surname == null) throw new NullPointerException("surname");
        if (builder.ssn == null) throw new NullPointerException("ssn");
        this.firstName = builder.firstName;
        this.surname = builder.surname;
        this.ssn = builder.ssn;
    }

    public String getFirstName() { return firstName;  }
    public String getSurname() { return surname; }
    public String getSsn() { return ssn; }    
}

import static com.acme.CustomerBuilder.customer;

public class Client {
    public void doSomething() {
        Customer customer = customer()
            .withSurname("Smith")
            .withFirstName("Fred")
            .withSsn("123XS1")
            .build();
    }
}