Esto está motivado por esta respuesta a una pregunta separada .
El patrón de construcción se usa para simplificar la inicialización compleja, especialmente con parámetros de inicialización opcionales). Pero no sé cómo administrar adecuadamente las configuraciones mutuamente excluyentes.
Aquí hay una Image
clase. Image
se puede inicializar desde un archivo o desde un tamaño, pero no ambos . Usar constructores para imponer esta exclusión mutua es obvio cuando la clase es lo suficientemente simple:
public class Image
{
public Image(Size size, Thing stuff, int range)
{
// ... initialize empty with size
}
public Image(string filename, Thing stuff, int range)
{
// ... initialize from file
}
}
Ahora suponga Image
que en realidad es lo suficientemente configurable para que el patrón de construcción sea útil, de repente esto podría ser posible:
Image image = new ImageBuilder()
.setStuff(stuff)
.setRange(range)
.setSize(size) // <---------- NOT
.setFilename(filename) // <---------- COMPATIBLE
.build();
Estos problemas deben detectarse en tiempo de ejecución en lugar de en tiempo de compilación, que no es lo peor. El problema es que la detección sistemática y exhaustiva de estos problemas dentro de la ImageBuilder
clase podría ser compleja, especialmente en términos de mantenimiento.
¿Cómo debo lidiar con configuraciones incompatibles en el patrón de construcción?
Range
yStuff
deben inicializarse al principio, no en momentos arbitrarios.RangeAndStuffBuilder
) se pueden invocar en el tipo real. Se pueden implementar restricciones adicionales al devolver más tipos basales para algunos métodos (aunque esto causará un aumento exponencial en los tipos), eliminando efectivamente las operaciones. Mientras los resultados del método no vuelvan a descender en la jerarquía, no obtendrá errores de tipo. El escenariosetHeight
/setWidth
podría implementarse con una jerarquía de hermanos que no tiene unbuild
método.