Come faccio a creare un builder in C # per un oggetto con proprietà che sono tipi di riferimento?

StackOverflow https://stackoverflow.com/questions/1402975

Domanda

Ho iniziato a costruire un builder in modo da poter creare facilmente i dati di test per unit test che sto scrivendo.

La struttura di base del builder è:

public class MyClassBuilder
{
    public int id = 0; //setting the default value here

    //allows MyClass to be built with a specific id
    public MyClassBuilder WithId(int id)
    {
        this.id = id;
        return this;
    }

    public MyClass Build()
    {
        return new MyClass(id);
    }
}

L'utilizzo di questo modello diventa quindi:

MyClass mc = new MyClassBuilder().WithId(5).Build();

Sono a mio agio con questo ... ma dove ho domande è quando MyClass ha una proprietà non banale .... Sono un po 'incerto su come procedere con la costruzione con un valore predefinito.

public class MyClassBuilder
{
    public int id = 0; //setting the default value here

    //do I construct the default value using a MySecondClassBuilder?
    public MySecondClass mySecondClass;

    //allows MyClass to be built with a specific id
    public MyClassBuilder WithId(int id)
    {
        this.id = id;
        return this;
    }

    public MyClassBuilder WithMySecondClass(MySecondClass mySecondClass)
    {
        this.mySecondClass = mySecondClass;
    }

    public MyClass Build()
    {
        return new MyClass(id);
    }
}

La mia ipotesi è che vorrei creare un builder per MySecondClass e usarlo per creare l'implementazione predefinita.

Qualcuno può confermare che la mia ipotesi è corretta ed è la migliore pratica?

Attualmente sto testando la mia ipotesi, ma ho pensato di documentare questa idea su StackOverflow poiché gli unici esempi di scalpellatura del costruttore che sono riuscito a trovare utilizzando Google solo per le proprietà costruite che sono tipi di valore e non tipi di riferimento.

È stato utile?

Soluzione

Come per la maggior parte delle cose, dipende.

Se vuoi la semantica di riferimento nel campo mySecondClass (ovvero: vuoi salvare un riferimento in un'altra classe), allora è esattamente come il tuo numero intero. Avere il valore predefinito su null potrebbe essere perfettamente accettabile.

Tuttavia, se vuoi garantire sempre un valore, dovrai costruire o semplicemente costruire un nuovo oggetto per la tua seconda classe.

Inoltre, se si desidera clonare anziché copie di riferimento, è necessario costruire un nuovo oggetto e assegnarlo al riferimento.

Detto questo, in molti casi, i costruttori tradizionali sono più appropriati che provare a creare un'interfaccia fluida in stile build. È davvero facile "fare confusione". un'interfaccia fluida per la costruzione di questo tipo, dal momento che fai affidamento sull'utente più che su un costruttore tradizionale.

Ad esempio, nel tuo codice, cosa succede se l'utente non chiama mai Build? Restituisce ancora qualcosa (poiché ciascuno dei metodi restituisce un oggetto), ma è valido? Non sarei sicuro come consumatore della tua classe. I costruttori tradizionali con sovraccarichi, d'altra parte, mi mostrano esattamente ciò che è valido e ciò che non è valido e vengono applicati in fase di compilazione.

Altri suggerimenti

Una volta che ho un oggetto complesso in cui voglio un manichino da usare nei test unitari, mi rivolgerò generalmente a un quadro beffardo / falso / di isolamento. In C #, la mia preferenza è Moq, ma ce ne sono diversi là fuori, come Rhino Mocks, Typemock e così via.

Il vantaggio è che in genere avranno la capacità di stubare tutte le proprietà esistenti nel tipo originale con valori predefiniti e oggetti vuoti, senza che tu debba fare altro che una singola riga per creare l'oggetto falso. Allo stesso tempo, è possibile configurare facilmente l'oggetto per restituire i valori desiderati per quelli delle proprietà che contano per il test. Inoltre, la maggior parte di tali framework supporta la falsificazione ricorsiva, in modo che anche gli oggetti complessi all'interno del tuo tipo vengano falsificati e così via.

Ci sono ragioni per cui uno potrebbe non voler usare un framework separato, nel qual caso penso che il suggerimento di Reed Copsey sarebbe il modo manuale per farlo.

Se non ci sono ragioni per evitare l'aggiunta di un framework beffardo (che sarebbe solo una dipendenza dai progetti di test, supponendo che tu abbia suddiviso i test), lo consiglierei vivamente per tipi complessi.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top