Programación por contrato (contract programming)

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.

La expresión 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!");

Contratos en las funciones

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.

Pruebas invariantes

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:

  • Se llaman después del constructor y antes del destructor.
  • Se llaman antes de entrar dentro de una función miembro.
  • Se llaman después de salir de una función miembro.

Validar la entrada de usuario

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.

En profundidad

rdmd playground.d