使用するのに適した設計はどれですか。1つはnullを許可するコンストラクタ、もう1つはnullにArgumentNullException
をスローする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;
}
}
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;
}
}
}
オブジェクトの構築時に有効な状態になるすべてのケースをカバーする最も単純なインターフェースを提供する必要があります。あなたはまた、あなたが受け入れることに寛大であるべきです。 1つのコンストラクターを提供することは、常に2つを提供することよりも簡単です。このクラスを使用するコードの作成者は、追加の決定を負担することはありません。
_barがクラスの他のメソッド/プロパティから何らかの方法で公開されている場合は、単一のコンストラクターに引数としてnullが渡されたときの動作についてのドキュメントコメントでコンストラクターをドキュメント化します(つまり、バーはデフォルトで空です)。 nullが渡されます)。
ファクトリーメソッドを使用することをお勧めします。意図(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です)
デフォルト値を示すための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
。
私は最初のケースです。
2番目のケースでは、null
を有効なパラメーターとして昇格します。 IMO null
は、値がないことを示すために使用しないでください。これがOption
型が存在する理由です(StuartLCが示唆)。
正直なところ、私はどちらにも圧倒されていません。
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;
}
}
ユーザーは、パラメーターを指定するか、パラメーターなしのコンストラクターを呼び出すことができます。そして、見落としによって、プロパティが初期化されずに呼び出された場合、遅延インスタンス化によってその場でプロパティが作成されます。