web-dev-qa-db-ja.com

コンストラクターのオーバーロードまたはnullの許可?

使用するのに適した設計はどれですか。1つはnullを許可するコンストラクタ、もう1つはnullにArgumentNullExceptionをスローする2つのコンストラクタですか。

例外をスローする2つのコンストラクター

public class Foo
{
    private IDictionary<string, string> _bar;

    public Foo()
    {
        _bar = new Dictionary<string, string>();
    }

    public Foo(IDictionary<string, string> bar)
    {
        if (bar == null) { throw new ArgumentNullException(); }
        _bar = bar;
    }
}

Nullを処理する1つのコンストラクター

public class Foo
{
    private IDictionary<string, string> _bar;

    public Foo(IDictionary<string, string> bar = null)
    {
        if (bar == null) {
            _bar = new Dictionary<string, string>();
        } else {
            _bar = bar;
        }
    }
}
3
Fred

オブジェクトの構築時に有効な状態になるすべてのケースをカバーする最も単純なインターフェースを提供する必要があります。あなたはまた、あなたが受け入れることに寛大であるべきです。 1つのコンストラクターを提供することは、常に2つを提供することよりも簡単です。このクラスを使用するコードの作成者は、追加の決定を負担することはありません。

_barがクラスの他のメソッド/プロパティから何らかの方法で公開されている場合は、単一のコンストラクターに引数としてnullが渡されたときの動作についてのドキュメントコメントでコンストラクターをドキュメント化します(つまり、バーはデフォルトで空です)。 nullが渡されます)。

2
whaley

ファクトリーメソッドを使用することをお勧めします。意図(createEmpty()createWithValues())を記述し、呼び出し元に期待する内容を公開できます:createWithValues()-nullは許可されていません。その場合、コンストラクターはプライベートになり、createWithValues()のみがnullチェックまたはその他の値チェックを含みます。

class Foo {
    private Map<String, String> bar;
    private Foo(final Map<String, String> bar) {
       this.bar = bar;
    }

    public static Foo createEmpty() {
       return new Foo(new HashMap<>());
    } 

    public static Foo createWithValue(final Map<String, String> values) {
       if (values == null) throw new YourFavoriteExceptionForThisCase();
       return new Foo(values)
    }
}

シンプルでクリーン。 (申し訳ありませんが、コードはJavaです)

2
morpheus05

デフォルト値を示すためのnullの使用は、C#のC/C++ルートに到達する確立されたプリンシパルであることを示していますが、別のアプローチは Optionの概念を借用することです。タイプ (HaskellではMaybeと呼ばれます)

これにより、呼び出し側はデフォルトが必要かどうかを明示的に指定する必要があります。これにより、意図的にnullを不正なパラメーターとして渡し、nullを使用してオプションであることを示すという混乱を回避できます。

次に、「2つのコンストラクター」コードの両方の懸念をカバーする単一のコンストラクターを用意します。

public Foo(Option<IDictionary<string, string>> optionalBar)
{
    if (bar == null) 
       throw ArgumentNullException();
    if (bar.HasValue) {
        _bar = new Dictionary<string, string>();
    } else {
        _bar = bar.Value;
    }
}

残念ながら、C#は、FP言語と言うほどうまく流れません。これは、パターンマッチングを使用してSome<Dictionary>およびNone

0
StuartLC

私は最初のケースです。

2番目のケースでは、nullを有効なパラメーターとして昇格します。 IMO nullは、値がないことを示すために使用しないでください。これがOption型が存在する理由です(StuartLCが示唆)。

0
Euphoric

正直なところ、私はどちらにも圧倒されていません。

1つ目は予期しない例外をスローする可能性があり、2つ目はパラメーターとしても提供されていないメンバー変数を初期化します。コンストラクターは予期しない動作を引き起こすべきではありません。

私はこのようなものを好みます:

public class Foo
{
    private IDictionary<string, string> _bar;

    private IDictionary<string, string> Bar
    {
        get
        {
            if (_bar == null)
                _bar = new Dictionary<string, string>();

            return _bar;
        }

        set { _bar = value; }

    }

    public Foo()
    {

    }

    public Foo(IDictionary<string, string> bar)
    {
        Bar = bar;
    }
}

ユーザーは、パラメーターを指定するか、パラメーターなしのコンストラクターを呼び出すことができます。そして、見落としによって、プロパティが初期化されずに呼び出された場合、遅延インスタンス化によってその場でプロパティが作成されます。

0
Robbie Dee