web-dev-qa-db-ja.com

オプションのパラメーターまたはオーバーロードされたコンストラクター

私はDelegateCommandを実装しています。コンストラクタを実装しようとしたときに、次の2つの設計上の選択肢を思いつきました。

1:複数のオーバーロードされたコンストラクターがある

public DelegateCommand(Action<T> execute) : this(execute, null) { }

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
{
    this.execute = execute;
    this.canExecute = canExecute;
}

2:オプションのパラメーターを持つコンストラクターを1つだけ持つ

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute = null)
{
    this.execute = execute;
    this.canExecute = canExecute;
}

可能な利点/欠点がわからないため、どちらを使用するかわかりません。提案された2つの方法のどちらかが付属しています。どちらも次のように呼び出すことができます:

var command = new DelegateCommand(this.myExecute);
var command2 = new DelegateCommand(this.myExecute, this.myCanExecute);

誰かが私を正しい方向に向けてフィードバックを提供できますか?

28
Thomas Flinkow

私はデフォルト値よりも複数のコンストラクターを好み、個人的には2つのコンストラクターの例は好きではありません。実装方法は異なるはずです。

複数のコンストラクターを使用する理由は、メインコンストラクターがすべてのパラメーターがnullでないかどうか、およびそれらが有効かどうかを確認できるだけであるのに対し、他のコンストラクターはメインコンストラクターのデフォルト値を提供できるためです。

ただし、例では、2番目のコンストラクターでもデフォルト値としてnullを渡し、1次コンストラクターもデフォルト値を知っている必要があるため、両者の間に違いはありません。私はそうすべきではないと思います。

これは、この方法で実装した場合、よりクリーンでより適切に分離されることを意味します。

public DelegateCommand(Action<T> execute) : this(execute, _ => true) { }

public DelegateCommand(Action<T> execute, Func<T, bool> canExecute)
{
    this.execute = execute ?? throw new ArgumentNullException(..);
    this.canExecute = canExecute ?? throw new ArgumentNullException(..);
}

nullのすべてのパラメーターもチェックするようになり、デフォルトを気にしないプライマリコンストラクターに渡された_ => trueに注意してください。


ただし、最も重要な点は拡張性です。複数のコンストラクターは、将来コードを拡張する可能性がある場合に、より安全です。必要なパラメータをさらに追加し、オプションのパラメータを最後に置く必要がある場合、現在のすべての実装が中断されます。古いコンストラクタを[Obsolete]にして、削除されることをユーザーに通知して、コードをすぐに壊すことなく新しい実装に移行する時間を与えることができます。


一方、オプションのパラメータを多くしすぎると、混乱が生じる可能性があります。あるシナリオでは必須のパラメータがあり、別のシナリオではオプションのパラメータが必要な場合は、パラメータを調べるだけで適切なコンストラクタを選択するのではなく、ドキュメントを調べる必要があるためです。

24
t3chb0t

コンストラクターで行うのは単純な割り当てだけであることを考えると、単一コンストラクターソリューションの方が適している場合があります。他のコンストラクターは追加の機能を提供せず、2つのパラメーターを指定したコンストラクターの設計から、2番目の引数を指定する必要がないことは明らかです。

さまざまなタイプからオブジェクトを構築する場合、複数のコンストラクターは理にかなっています。その場合、コンストラクターは入力ファクトリーを処理し、構築中のクラスの正しい内部プロパティ表現にフォーマットするため、外部ファクトリーの代わりになります。しかし、それはあなたのケースでは起こりません、それ故に単一のコンストラクタで十分である理由です。

17
Andy

それぞれのアプローチが優れているケースは2つあると思います。

まず、単純なコンストラクターがある場合(これは、私の経験では通常そうです)、オプションの引数が優れていると考えます。

  1. それらは、書かれなければならない(そしてそれゆえ、それはまた読まれなければならない)コードを最小化します。
  2. ドキュメンテーションが1か所にあり、繰り返されないようにすることができます(古くなる可能性のある余分な領域が開くため、これは悪いことです)。
  3. 多くのオプションの引数がある場合は、コンストラクターの組み合わせが非常に混乱することを回避できます。 2つのオプションの引数(互いに無関係)が2つしかない場合でも、個別のオーバーロードコンストラクターが必要な場合は、4つのコンストラクター(バージョンなし、バージョンあり、バージョンあり、両方あり)が必要です。これは明らかにうまくスケーリングしません。

Buuuut、コンストラクターのオプションの引数が明らかに混乱を招く場合があります。明白な例は、これらのオプションの引数が排他的である(つまり、一緒に使用できない)場合です。引数の実際に有効な組み合わせのみを許可するオーバーロードされたコンストラクターがあると、この制約がコンパイル時に適用されます。とはいえ、この場合も発生しないようにする必要があります(たとえば、複数のクラスがベースから継承し、各クラスが排他的な動作を行う場合)。

3
Kat