¿Cómo creo un constructor en C # para un objeto que tiene propiedades que son tipos de referencia?

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

Pregunta

Empecé a construir un constructor para poder crear fácilmente datos de prueba para pruebas unitarias que estoy escribiendo.

La estructura básica del constructor es:

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

El uso de este patrón se convierte en:

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

Me siento cómodo con eso ... pero cuando tengo preguntas es cuando MyClass tiene una propiedad que no es trivial ... Estoy un poco inseguro acerca de cómo construir con un valor predeterminado.

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

Supongo que crearía un generador para MySecondClass y lo utilizaría para crear la implementación predeterminada.

¿Puede alguien confirmar que mi suposición es correcta y es la mejor práctica?

Actualmente estoy en el proceso de probar mi suposición, pero pensé en documentar esta idea en StackOverflow ya que los únicos ejemplos del patrón de constructor que pude encontrar usando Google solo construyeron propiedades que son tipos de valor y no tipos de referencia.

¿Fue útil?

Solución

Como con la mayoría de las cosas, depende.

Si desea semántica de referencia en el campo mySecondClass (es decir, desea guardar una referencia a otra clase), entonces es exactamente como su entero. Tener el valor predeterminado nulo podría ser perfectamente aceptable.

Sin embargo, si desea garantizar siempre un valor, deberá construir o simplemente construir un nuevo objeto para su segunda clase.

Además, si desea clonar en lugar de copias de referencia, necesitaría construir un nuevo objeto y asignarlo a la referencia.

Dicho esto, en muchos casos, los constructores tradicionales son más apropiados que intentar hacer una interfaz de estilo de construcción fluida. Es realmente fácil " desordenar " una interfaz fluida para construir de esta manera, ya que confía en el usuario más que en un constructor tradicional.

Como ejemplo, en su código, ¿qué sucede si el usuario nunca llama a Build? Todavía está devolviendo algo (ya que cada uno de los métodos devuelve un objeto), pero ¿es válido? No estaría seguro como un consumidor de su clase. Los constructores tradicionales con sobrecargas, por otro lado, me muestran exactamente qué es válido y qué no es válido, y se aplican en tiempo de compilación.

Otros consejos

Una vez que tengo un objeto complejo donde quiero que se use un maniquí en pruebas unitarias, generalmente recurriré a un marco de burla / simulación / aislamiento. En C #, mi preferencia es Moq, pero hay varios, como Rhino Mocks, Typemock, etc.

La ventaja es que normalmente tendrán la capacidad de eliminar todas las propiedades que existen en el tipo original con valores predeterminados y objetos vacíos, sin que tenga que hacer nada más que una sola línea para crear el objeto falso. Al mismo tiempo, puede configurar fácilmente el objeto para que devuelva los valores que desea para las propiedades que son importantes para su prueba. Además, la mayoría de estos marcos admiten la falsificación de forma recursiva, de modo que los objetos complejos dentro de su tipo también serán falsificados, y así sucesivamente.

Hay razones por las cuales uno no querría usar un marco separado, en cuyo caso creo que la sugerencia de Reed Copsey sería la forma manual de hacerlo.

Si no hay razones para evitar agregar un marco de burla (que solo sería una dependencia de tus proyectos de prueba, suponiendo que hayas dividido la prueba), lo recomendaría para los tipos complejos.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top