C'è qualche motivo nell'usare i costruttori e le interfacce fluide con gli inizializzatori di oggetti?

9

In Java e C #, è possibile creare un oggetto con proprietà che possono essere impostate all'inizializzazione definendo un costruttore con parametri, definendo ogni proprietà dopo la costruzione dell'oggetto o usando il modello di interfaccia generatore / fluido. Tuttavia, C # 3 ha introdotto inizializzatori di oggetti e raccolte, il che significava che il modello di builder era in gran parte inutile. In una lingua senza inizializzatori, è possibile implementare un builder quindi utilizzarlo in questo modo:

Vehicle v = new Vehicle.Builder()
                    .manufacturer("Toyota")
                    .model("Camry")
                    .year(1997)
                    .colour(CarColours.Red)
                    .addSpecialFeature(new Feature.CDPlayer())
                    .addSpecialFeature(new Feature.SeatWarmer(4))
                    .build();

Al contrario, in C # si potrebbe scrivere:

var vehicle = new Vehicle {
                Manufacturer = "Toyota",
                Model = "Camry",
                Year = 1997,
                Colour = CarColours.Red,
                SpecialFeatures = new List<SpecialFeature> {
                    new Feature.CDPlayer(),
                    new Feature.SeatWarmer { Seats = 4 }
                }
              }

... eliminando la necessità di un builder come nell'esempio precedente.

Sulla base di questi esempi, i builder sono ancora utili in C # o sono stati sostituiti interamente dagli inizializzatori?

    
posta svbnet 01.10.2016 - 08:00
fonte

3 risposte

11

Come toccato da @ user248215 il vero problema è l'immutabilità. Il motivo per cui utilizzeresti un builder in C # sarebbe quello di mantenere l'esplicitazione di un inizializzatore senza dover esporre le proprietà impostabili. Non è una questione di incapsulamento, ecco perché ho scritto la mia risposta. L'incapsulamento è abbastanza ortogonale poiché invocare un setter non implica ciò che il settatore effettivamente fa o lega alla sua implementazione.

La prossima versione di C #, 8.0, introdurrà probabilmente una parola chiave with che consentirà l'inizializzazione di oggetti immutabili in modo chiaro e conciso senza la necessità di scrivere builder.

Un'altra cosa interessante che puoi fare con i builder, al contrario degli inizializzatori, è che possono generare diversi tipi di oggetti a seconda della sequenza di metodi chiamata.

Ad esempio

value.Match()
    .Case((DateTime d) => Console.WriteLine($"{d: yyyy-mm-dd}"))
    .Case((double d) => Console.WriteLine(Math.Round(d, 4));
    // void

var str = value.Match()
    .Case((DateTime d) => $"{d: yyyy-mm-dd}")
    .Case((double d) => Math.Round(d, 4).ToString())
    .ResultOrDefault(string.Empty);
    // string

Giusto per chiarire l'esempio sopra, è una libreria di pattern matching che usa il pattern builder per creare una "corrispondenza" specificando i casi. I casi vengono aggiunti chiamando il metodo Case passandogli una funzione. Se value è assegnabile al tipo di parametro della funzione, viene richiamato. Puoi trovare il codice sorgente completo su GitHub e, dal I commenti XML sono difficili da leggere in testo normale, ecco un link a la documentazione costruita da SandCastle (vedi Commento sezione)

    
risposta data 01.10.2016 - 09:54
fonte
11

Gli inizializzatori degli oggetti richiedono che le proprietà siano accessibili dal codice chiamante. I builder nidificati possono accedere ai membri privati della classe.

Se si desidera rendere Vehicle immutable (rendendo privati tutti i setter), è possibile utilizzare il builder nidificato per impostare le variabili private.

    
risposta data 01.10.2016 - 08:07
fonte
0

Hanno tutti scopi diversi !!!

I costruttori possono inizializzare i campi contrassegnati con readonly e i membri privati e protetti. Tuttavia, sei un po 'limitato in ciò che puoi fare all'interno di un costruttore; dovresti evitare di passare this a qualsiasi metodo esterno, ad esempio, e dovresti evitare di chiamare membri virtuali, poiché possono essere eseguiti nel contesto di una classe derivata che non si è ancora costruita. Inoltre, i costruttori sono garantiti per l'esecuzione (a meno che il chiamante stia facendo qualcosa di molto insolito), quindi se si impostano i campi nel costruttore, il codice nel resto della classe può assumere che quei campi non saranno nulli.

Inizializzatori esegui dopo la costruzione. Ti permettono solo di chiamare proprietà pubbliche; non è possibile impostare campi privati e / o di sola lettura. Per convenzione, l'atto di impostare una proprietà dovrebbe essere piuttosto limitato, ad es. dovrebbe essere idempotente e avere effetti collaterali limitati.

I metodi

Builder sono metodi effettivi e pertanto consentono più di un argomento e, per convenzione, possono avere effetti collaterali che includono la creazione di oggetti. Sebbene non possano impostare campi di sola lettura, possono praticamente fare qualsiasi altra cosa. Inoltre, i metodi possono essere implementati come metodi di estensione (come quasi tutte le funzionalità LINQ).

    
risposta data 19.07.2017 - 19:02
fonte

Leggi altre domande sui tag