Pondría la API fluida a su propia clase "constructora" separada del objeto que está creando. De esa manera, si el cliente no quiere usar la API fluida, aún puede usarla manualmente y no contamina el objeto de dominio (adhiriéndose al principio de responsabilidad única). En este caso se crearía lo siguiente:
Car
cual es el objeto de dominio
CarBuilder
que contiene la API fluida
El uso sería así:
var car = CarBuilder.BuildCar()
.OfBrand(Brand.Ford)
.OfModel(12345)
.PaintedIn(Color.Silver)
.Build();
La CarBuilder
clase se vería así (estoy usando la convención de nomenclatura de C # aquí):
public class CarBuilder {
private Car _car;
/// Constructor
public CarBuilder() {
_car = new Car();
SetDefaults();
}
private void SetDefaults() {
this.OfBrand(Brand.Ford);
// you can continue the chaining for
// other default values
}
/// Starts an instance of the car builder to
/// build a new car with default values.
public static CarBuilder BuildCar() {
return new CarBuilder();
}
/// Sets the brand
public CarBuilder OfBrand(Brand brand) {
_car.SetBrand(brand);
return this;
}
// continue with OfModel(...), PaintedIn(...), and so on...
// that returns "this" to allow method chaining
/// Returns the built car
public Car Build() {
return _car;
}
}
Tenga en cuenta que esta clase no será segura para subprocesos (cada subproceso necesitará su propia instancia de CarBuilder). También tenga en cuenta que, aunque la API fluida es un concepto realmente genial, probablemente sea excesivo con el fin de crear objetos de dominio simples.
Este acuerdo es más útil si está creando una API para algo mucho más abstracto y tiene una configuración y ejecución más compleja, por lo que funciona muy bien en pruebas unitarias y marcos DI. Puedes ver algunos otros ejemplos en la sección Java del artículo de Wikipedia sobre la interfaz fluida con persistencia, manejo de fechas y objetos simulados.
EDITAR:
Como se señaló en los comentarios; podría hacer que la clase Builder sea una clase interna estática (dentro de Car) y Car podría hacerse inmutable. Este ejemplo de dejar que el coche sea inmutable parece un poco tonto; pero en un sistema más complejo, donde absolutamente no desea cambiar el contenido del objeto que se construye, es posible que desee hacerlo.
A continuación se muestra un ejemplo de cómo hacer la clase interna estática y cómo manejar una creación de objeto inmutable que construye:
// the class that represents the immutable object
public class ImmutableWriter {
// immutable variables
private int _times; private string _write;
// the "complex" constructor
public ImmutableWriter(int times, string write) {
_times = times;
_write = write;
}
public void Perform() {
for (int i = 0; i < _times; i++) Console.Write(_write + " ");
}
// static inner builder of the immutable object
protected static class ImmutableWriterBuilder {
// the variables needed to construct the immutable object
private int _ii = 0; private string _is = String.Empty;
public void Times(int i) { _ii = i; }
public void Write(string s) { _is = s; }
// The stuff is all built here
public ImmutableWriter Build() {
return new ImmutableWriter(_ii, _is);
}
}
// factory method to get the builder
public static ImmutableWriterBuilder GetBuilder() {
return new ImmutableWriterBuilder();
}
}
El uso sería el siguiente:
var writer = ImmutableWriter
.GetBuilder()
.Write("peanut butter jelly time")
.Times(2)
.Build();
writer.Perform();
// console writes: peanut butter jelly time peanut butter jelly time
Edición 2: Pete en los comentarios hizo una publicación de blog sobre el uso de constructores con funciones lambda en el contexto de escribir pruebas unitarias con objetos de dominio complejos. Es una alternativa interesante para hacer que el constructor sea un poco más expresivo.
En el caso de CarBuilder
que necesite tener este método en su lugar:
public static Car Build(Action<CarBuilder> buildAction = null) {
var carBuilder = new CarBuilder();
if (buildAction != null) buildAction(carBuilder);
return carBuilder._car;
}
Que se puede usar así:
Car c = CarBuilder
.Build(car =>
car.OfBrand(Brand.Ford)
.OfModel(12345)
.PaintedIn(Color.Silver);
var car = new Car(Brand.Ford, 12345, Color.Silver);
?