Costruttori pubblici e privati con firme equivalenti

1

Ecco il problema illustrato usando un esempio di una classe immutabile. Un libro deve avere almeno uno di un titolo e un codice 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.
}

Vedi che privateConstructor booleano? Ho dovuto lanciarlo per distinguere tra pubblico Book(string, int) e privato Book(string, int?) . Mentre questo è abbastanza innocuo dal momento che il parametro estraneo è in un costruttore privato, il suo uso mi sembra maleodorante. Riordinare i parametri del costruttore privato funzionerebbe ma ciò renderebbe gli inizializzatori del costruttore non intuitivi (nel mio vero lavoro, ci sono più di due soli parametri in gioco). Cos'è un modo migliore?

    
posta krallus 06.11.2015 - 08:00
fonte

4 risposte

1

Questo sembra C # con parametri opzionali.

Dato che stai già generando eccezioni nei costruttori pubblici, puoi fare a meno di tutti quei costruttori tranne quello privato. Rendilo pubblico, rendi title facoltativo, rimuovi il parametro booleano e chiedi ai chiamanti di utilizzare parametri con nome.

L'altra cosa che puoi fare è semplicemente rendere al costruttore privato una chiamata al metodo ordinario, e dargli un nome diverso. In questo modo, non hai bisogno del parametro bool per disambigerlo.

    
risposta data 06.11.2015 - 08:16
fonte
1

Non esiste un modo diverso.

I costruttori hanno sempre un nome predeterminato (qualcosa come __init__ come in Python, o il nome della classe come in C ++ / Java / C #), quindi non può essere modificato per differenziare i due costruttori. Ciò significa che solo la lista degli argomenti può essere utilizzata per creare diversi overload.

Se vuoi delegare del lavoro a un altro costruttore (privato) che ha bisogno di una firma compatibile con quella di un costruttore pubblico, allora hai essenzialmente 3 opzioni:

  1. Aggiungi un parametro fittizio al costruttore privato, in modo che diventi un overload separato
  2. Unisci il costruttore pubblico e privato con le firme equivalenti
  3. Invece di delegare a un costruttore privato, delegare a una funzione privata e accettare che le inizializzazioni dei membri debbano essere duplicate.

Nessuna di queste opzioni è davvero elegante, quindi dovresti decidere caso per caso quale sia il meno brutto.

    
risposta data 06.11.2015 - 08:21
fonte
1

Rimuovi il tuo costruttore privato e fai la logica nell'ultimo costruttore pubblico:

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)
    }
}

Vorrei usare lo 0 per indicare che il codice ISBN non è stato dato. In alternativa, potrebbe essere meglio rendere _isbn non annullabile, perché il valore iniziale 0 indica che non esiste un codice ISBN. Quindi:

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)
    }
}

Migliore leggibilità.

    
risposta data 06.11.2015 - 10:58
fonte
0

C'è qualche ragione per cui non puoi usare metodi statici (qualcosa come createFromTitle e createFromISBN ) + constructructor privato per ciò che stai cercando di ottenere? Usa semplicemente quei metodi statici come controli.

    
risposta data 07.11.2015 - 09:19
fonte

Leggi altre domande sui tag