web-dev-qa-db-ja.com

同等の署名を持つパブリックコンストラクターとプライベートコンストラクター

これは、不変クラスの例を使用して説明されている問題です。書籍には、少なくとも1つのタイトルとISBNが必要です。

_public class Book
{
    private readonly string _title;
    private readonly int? _isbn;

    public Book(string title)
        : this(title, null, true)
    {
        ... throw exception if title is null
    }

    public Book(int isbn)
        : this(null, isbn, true)
    {
        ... throw exception if isbn < 1
    }

    public Book(string title, int isbn)
        : this(title, isbn, true)
    {
        ... throw exception if title is null
        ... throw exception if isbn < 1
    }

    private Book(string title, int? isbn, bool privateConstructor = true)
    {
        _title = title;
        _isbn = isbn;
        ... more work (beyond the scope of this example)
    }

    ... public properties, etc.
}
_

privateConstructorブール値を参照してください? public Book(string, int)とprivateBook(string, int?)を区別するためにそれを投入する必要がありました。無関係なパラメーターはプライベートコンストラクターにあるため、これはかなり無害ですが、その使用は私には臭いようです。プライベートコンストラクターのパラメーターを並べ替えることは機能しますが、コンストラクターの初期化子が直感的ではないように見えます(私の実際の作業では、2つ以上のパラメーターが機能しています)。より良い方法は何ですか?

1
krallus

これは、オプションのパラメーターを持つC#のように見えます。

パブリックコンストラクターですでに例外をスローしているので、プライベートコンストラクターを除くすべてのコンストラクターを省略できます。公開し、titleをオプションにし、ブールパラメータを削除し、呼び出し元に名前付きパラメータを使用させます。

もう1つできることは、プライベートコンストラクターを代わりに通常のメソッド呼び出しにして、別の名前を付けることです。そうすれば、明確にするためにboolパラメーターは必要ありません。

1
Robert Harvey

実際には別の方法はありません。

コンストラクターには常に事前に定義された名前(Pythonのように__init__のようなもの、またはC++/Java/C#のようにクラス名)があるため、2つのコンストラクターを区別するために変更することはできません。つまり、引数リストのみを使用してさまざまなオーバーロードを作成できます。

パブリックコンストラクターの署名と互換性のある署名が必要な別の(プライベート)コンストラクターに作業を委任する場合は、基本的に3つのオプションがあります。

  1. プライベートコンストラクターにダミーパラメーターを追加して、個別のオーバーロードになるようにします
  2. パブリックコンストラクタとプライベートコンストラクタを同等のシグネチャとマージします
  3. プライベートコンストラクターに委任する代わりに、プライベート関数に委任し、メンバーの初期化を複製する必要があることを受け入れます。

これらのオプションはどちらも本当にエレガントではないので、最も醜いものではないケースバイケースで決定する必要があります。

プライベートコンストラクターを削除し、最後のパブリックコンストラクターでロジックを実行します。

public class Book
{
    private readonly string _title;
    private readonly int? _isbn;

    public Book(string title)
        : this(title, 0)
    {
    }
    public Book(int isbn)
        : this(null, isbn)
    {
    }

    public Book(string title, int isbn)
    {
        ... throw exception if title is null AND isbn < 1
        ... throw exception if isbn < 0

        _title = title;
        _isbn = isbn > 1 ? isbn : null;
        ... more work (beyond the scope of this example)
    }
}

0を使用して、ISBNが指定されていないことを示します。または、初期値0はISBNがないことを示すため、_isbnをnull許容にしない方がよい場合があります。そう:

public class Book
{
    private readonly string _title;
    private readonly int _isbn;

    public Book(string title)
        : this(title, 0)
    {
    }
    public Book(int isbn)
        : this(null, isbn)
    {
    }

    public Book(string title, int isbn)
    {
        ... throw exception if title is null AND isbn < 1
        ... throw exception if isbn < 0

        _title = title;
        _isbn = isbn;
        ... more work (beyond the scope of this example)
    }
}

読みやすさの点で優れています。

1
Martin Braun

静的メソッド(createFromTitlecreateFromISBNなど)+プライベートconstructructorを使用できない理由はありますか?これらの静的メソッドをコンストラクターとして使用するだけです。

0