Archivos mensuales: Junio 2011

Take it easy, we have builders

As I said before, it’s a valuable experience for me to keep in touch with different development teams.

Last week we were discussing the best method to validate domain model objects. My first response to that question is: model objects should never be invalid, in the first place.

Each domain object should be fully initialized in its constructor, either by initializing default values or receiving values ​​from the constructor.

Sometimes someone says that does not appreciate consructors with 20 parameters, then I question if the object is large because one or more objects are not yet discovered.

Not to make this post too long, suppose we agree that completely initialize the object with valid values ​​in the constructor and that no public method allows us to modify the state leaving it invalid. In this case, it is never necessary to validate our object because we know that it can never be invalid.

And here we come to the practice recommended to fellow developers last week: if it’s not trivial to validate a data and, once validated, to build the object, then use a Builder. Builder’s responsibilities are:

  • Make sure you have all the necessary data to construct the object
  • Provide a friendly interface for specifying the data
  • Construct the object

The following example “fluent API”:

public class InvoiceBuilder {
    private Customer _customer;
    private DateTime _date;

    public InvoiceBuilder WithCustomer(Customer customer) {
        _customer = customer;
        return this;
    }

    public InvoiceBuilder WithDate(DateTime date) {
        _date = date;
        return this;
    }

    public Invoice Build() {
        AssertDataCompleteAndValid();
        return new Invoice(_customer, _date);
    }

    private void AssertDataCompleteAndValid() {
        if (_customer == null)
            throw new ArgumentNullException("customer");

        if (_date < DateTime.Today)
            throw new ArgumentOutOfRangeException("date");
    }
}

And this code would be used this way:

var invoice = new InvoiceBuilder().
    WithCustomer(jose).
    WithDate(DateTime.Today).
    Build();

See you!

No desesperen, tenemos los builders

Como ya he dicho antes, el estar en contacto con equipos de desarrollo me resulta una experiencia muy valiosa.

Estábamos la semana pasada discutiendo sobre el mejor método para validar objetos del modelo de dominio. Mi primera respuesta ante esa pregunta es: nunca los objetos del modelo debieran estar inválidos, en primer lugar.

Cada objeto de dominio debiera inicializarse completamente en su constructor, ya sea inicializando valores por defecto, ya sea recibiendo valores desde el inicializador.

Algunas veces alguien menciona que no aprecia los constructores con 20 parámetros a lo cual respondo con un cuestionamiento sobre si realmente ese objeto debe ser tan “grande” o si hay uno o mas objetos no descubiertos aun.

Para no hacer esta entrada demasiado larga, supongamos que acordamos que inicializamos completamente el objeto, con valores válidos en el constructor y que ningún método publico nos permite modificarlo dejándolo en estado invalido (1). En este caso, nunca es necesario validar nuestro objeto puesto que sabemos que, si esta instanciado, nunca puede ser inválido.

Y aquí llegamos a la práctica que recomendé a unos colegas la semana pasada: si no es trivial validar una serie de datos y, una vez validados, construir el objeto, entonces usen un Builder. Las responsabilidades del Builder son varias:

  • Asegurarse de tener todos los datos necesarios para construir el objeto
  • Proporcionar una interfaz amigable para la especificación de todos los datos
  • Construir el objeto

A continuación ejemplo con “fluent API”:

public class FacturaBuilder {
    private Cliente _cliente;
    private DateTime _fecha;

    public FacturaBuilder ConCliente(Cliente cliente) {
        _cliente = cliente;
        return this;
    }

    public FacturaBuilder ConFecha(DateTime fecha) {
        _fecha = fecha;
        return this;
    }

    public Factura Build() {
        AssertDatosSuficientesYCorrectos();
        return new Factura(_cliente, _fecha);
    }

    private void AssertDatosSuficientesYCorrectos() {
        if (_cliente == null)
            throw new ArgumentNullException("cliente");

        if (_fecha < DateTime.Today)
            throw new ArgumentOutOfRangeException("fecha");
    }
}

Y este código se usaría de esta manera:

var factura = new FacturaBuilder().
    ConCliente(jose).
    ConFecha(DateTime.Today).
    Build();

Nos vemos!