La programación por contrato (contract programming en inglés) en D incluye
un conjunto de construcciones del lenguaje que permiten incrementar la calidad
del código implementando pruebas para asegurarse de que el código base se
comporta como se requiere. Los contratos (contracts en inglés) se compilan
y se ejecutan cuando se construye el software para pruebas o depuración.
Cuando se compila para lanzar una versión (activado en el compilador mediante
la opción -release
), estos contratos son totalmente omitidos por el
compilador, por lo que no se deberían usar para validar la entrada de usuario
o como alternativa a las excepciones.
assert
La forma más simple de programación por contrato en D es la expresión
assert(...)
, que prueba que se cumple cierta condición, abortando la
ejecución del programa mediante un AssertError
cuando esta no se satisface.
assert(sqrt(4) == 2);
// El mensaje de error es opcional.
assert(sqrt(16) == 4, "¡sqrt está roto!");
Las palabras reservadas in
y out
permiten formalizar estos contratos
para parámetros de entrada y valores de retorno de las funciones.
long square_root(long x)
in {
assert(x >= 0);
} out (result) {
assert((result * result) <= x
&& (result+1) * (result+1) > x);
} do {
return cast(long)std.math.sqrt(cast(real)x);
}
El contenido dentro del bloque in
también se puede poner dentro del cuerpo
de la función, pero la intención queda mucho más clara de esta forma. En el
bloque out
, el valor de retorno de la función se puede capturar mediante la
expresión out(result)
y verificarlo en consecuencia.
Mediante la palabra reservada invariant()
se declara una función miembro
especial dentro de las estructuras y de las clases que permiten realizar
pruebas sobre el estado del objeto durante toda su vida. Los invariantes:
Como todos los contratos se eliminan en las versiones finales, la entrada de
usuario no se debería validar mediante esta técnica. Por otra parte, las
expresiones assert
sí se pueden usar en funciones marcadas como nothrow
,
ya que no lanzan excepciones sino errores fatales. El análogo en tiempo de
ejecución a assert
es la función std.exception.enforce
de la librería estándar, ya que lanza excepciones que se pueden atrapar.