
El diseño por contrato ( DbC ), también conocido como programación por contrato , programación por contrato y programación de diseño por contrato , es un enfoque para el diseño de software .
Establece que los diseñadores de software deben definir especificaciones de interfaz formales , precisas y verificables para los componentes de software , que extienden la definición ordinaria de tipos de datos abstractos con precondiciones , postcondiciones e invariantes . Estas especificaciones se denominan "contratos", en consonancia con la metáfora conceptual de las condiciones y obligaciones de los contratos comerciales.
El enfoque DbC presupone que todos los componentes cliente que invocan una operación en un componente servidor cumplirán las condiciones previas especificadas como necesarias para esa operación.
Cuando esta suposición se considera demasiado arriesgada (como en la computación multicanal o distribuida ), se adopta el enfoque inverso , lo que significa que el componente del servidor comprueba que se cumplen todas las condiciones previas relevantes (antes o durante el procesamiento de la solicitud del componente del cliente ) y responde con un mensaje de error adecuado en caso contrario.
Historia
El término fue acuñado por Bertrand Meyer en relación con el diseño del lenguaje de programación Eiffel y se describió por primera vez en varios artículos a partir de 1986 [ 1 ] [ 2 ] [ 3 ] y en las dos ediciones sucesivas (1988, 1997) de su libro Object-Oriented Software Construction . Eiffel Software solicitó el registro de la marca comercial Design by Contract en diciembre de 2003, y se le concedió en diciembre de 2004. [ 4 ] [ 5 ] El propietario actual de esta marca comercial es Eiffel Software. [ 6 ] [ 7 ]
El diseño por contrato tiene sus raíces en trabajos sobre verificación formal , especificación formal y lógica de Hoare . Las contribuciones originales incluyen:
- Una metáfora clara para guiar el proceso de diseño.
- La aplicación a la herencia , en particular un formalismo para la redefinición y la vinculación dinámica.
- La aplicación al manejo de excepciones
- La conexión con la documentación automática del software
Descripción
La idea central de DbC es una metáfora sobre cómo los elementos de un sistema de software colaboran entre sí sobre la base de obligaciones y beneficios mutuos . La metáfora proviene del mundo empresarial, donde un "cliente" y un "proveedor" acuerdan un "contrato" que define, por ejemplo, que:
- El proveedor debe proporcionar un determinado producto (obligación) y tiene derecho a esperar que el cliente haya pagado su tarifa (beneficio).
- El cliente debe pagar la cuota (obligación) y tiene derecho a obtener el producto (beneficio).
- Ambas partes deben cumplir ciertas obligaciones, como las leyes y reglamentos aplicables a todos los contratos.
De manera similar, si el método de una clase en la programación orientada a objetos proporciona una determinada funcionalidad, puede:
- Cabe esperar que cualquier módulo cliente que lo llame garantice una determinada condición al entrar en el método: la precondición del método , una obligación para el cliente y un beneficio para el proveedor (el método en sí), ya que lo libera de tener que gestionar casos que no cumplan con la precondición.
- Garantizar una determinada propiedad al finalizar: la postcondición del método , una obligación para el proveedor y, obviamente, un beneficio (el principal beneficio de llamar al método) para el cliente.
- Mantener una determinada propiedad, asumida al entrar y garantizada al salir: el invariante de clase .
El contrato es semánticamente equivalente a una tripleta de Hoare que formaliza las obligaciones. Esto se puede resumir en las "tres preguntas" que el diseñador debe responder repetidamente en el contrato:
- ¿Qué estipula el contrato?
- ¿Qué garantiza el contrato?
- ¿Qué estipula el contrato?
Muchos lenguajes de programación cuentan con herramientas para realizar este tipo de aserciones . Sin embargo, DbC considera que estos contratos son tan cruciales para la corrección del software que deberían formar parte del proceso de diseño. De hecho, DbC aboga por escribir las aserciones primero . Los contratos pueden escribirse mediante comentarios en el código , verificarse mediante un conjunto de pruebas o ambas cosas, incluso si el lenguaje no ofrece soporte específico para contratos.
La noción de contrato se extiende hasta el nivel del método/procedimiento; el contrato para cada método normalmente contendrá la siguiente información:
- Valores o tipos de entrada aceptables e inaceptables, y sus significados.
- Valores o tipos de retorno y sus significados
- Valores o tipos de condiciones de error y excepción que pueden ocurrir y sus significados.
- Efectos secundarios
- Precondiciones
- Postcondiciones
- Invariantes
- (Más raramente) Garantías de rendimiento, por ejemplo, en cuanto al tiempo o el espacio utilizado.
En una jerarquía de herencia, las subclases pueden debilitar las precondiciones (pero no fortalecerlas) y fortalecer las postcondiciones e invariantes (pero no debilitarlas). Estas reglas se aproximan a la subtipificación de comportamiento .
Todas las relaciones de clase se establecen entre clases cliente y clases proveedor. Una clase cliente está obligada a realizar llamadas a las funcionalidades del proveedor, siempre que el estado resultante del proveedor no se vea afectado por la llamada del cliente. Posteriormente, el proveedor está obligado a proporcionar un estado y datos de retorno que no infrinjan los requisitos de estado del cliente.
Por ejemplo, un búfer de datos del proveedor puede requerir que los datos estén presentes en el búfer cuando se llama a una función de eliminación. Posteriormente, el proveedor garantiza al cliente que, cuando la función de eliminación finalice su trabajo, el elemento de datos se eliminará efectivamente del búfer. Otros contratos de diseño son los conceptos de invariante de clase . El invariante de clase garantiza (para la clase local) que el estado de la clase se mantendrá dentro de las tolerancias especificadas al final de cada ejecución de la función.
Al utilizar contratos, el proveedor verificará que se cumplan las condiciones del contrato, una práctica conocida como programación ofensiva , cuya idea general es que el código "falle estrepitosamente", siendo la verificación del contrato la red de seguridad.
La propiedad "fail hard" de DbC simplifica la depuración del comportamiento de los contratos, ya que el comportamiento previsto de cada método está claramente especificado.
Este enfoque difiere sustancialmente del de la programación defensiva , donde el proveedor es responsable de determinar qué hacer cuando se incumple una condición previa. En la mayoría de los casos, el proveedor lanza una excepción para informar al cliente de que se ha incumplido la condición previa, y en ambos casos —tanto en DbC como en programación defensiva— el cliente debe averiguar cómo responder. En tales casos, DbC facilita el trabajo del proveedor.
El diseño por contrato también define criterios de corrección para un módulo de software:
- Si la invariante de clase Y la precondición son verdaderas antes de que un cliente llame a un proveedor, entonces la invariante Y la postcondición serán verdaderas después de que se haya completado el servicio.
- Al realizar llamadas a un proveedor, un módulo de software no debe infringir las condiciones previas del proveedor.
El diseño por contrato también facilita la reutilización del código, ya que el contrato de cada fragmento de código está completamente documentado. Los contratos de un módulo pueden considerarse una forma de documentación del software que describe el comportamiento de dicho módulo.
El diseño por contrato, en C++ por ejemplo, se ve así: [ 8 ] [ 9 ]
int f ( const int x ) pre ( x != 1 ) // una aserción de precondición post ( r : r == x && r != 2 ) // una aserción de postcondición; r nombra el objeto resultante de f { contract_assert ( x != 3 ); // una declaración de aserción return x ; }Implicaciones del desempeño
Las condiciones contractuales nunca deben infringirse durante la ejecución de un programa sin errores. Por lo tanto, los contratos generalmente solo se verifican en modo de depuración durante el desarrollo del software. Posteriormente, en el momento del lanzamiento, las verificaciones de contratos se desactivan para maximizar el rendimiento.
En muchos lenguajes de programación, los contratos se implementan con assert . Los asserts se eliminan por defecto en la compilación en modo release en C/C++, y de manera similar se desactivan en C# [ 10 ] y Java.
Iniciar el intérprete de Python con " -O " (de "optimizar") como argumento hará que el generador de código Python no emita ningún bytecode para las aserciones. [ 11 ]
Esto elimina de forma efectiva los costes de ejecución de las aserciones en el código de producción, independientemente del número y el coste computacional de las aserciones utilizadas en el desarrollo, ya que el compilador no incluirá dichas instrucciones en la producción.
Relación con las pruebas de software
El diseño por contrato no sustituye las estrategias de prueba habituales, como las pruebas unitarias , las pruebas de integración y las pruebas de sistema . Más bien, complementa las pruebas externas con autopruebas internas que pueden activarse tanto para pruebas aisladas como en el código de producción durante una fase de prueba.
La ventaja de las autopruebas internas radica en que permiten detectar errores antes de que se manifiesten como resultados no válidos para el cliente. Esto conlleva una detección de errores más temprana y precisa.
El uso de aserciones puede considerarse una forma de oráculo de prueba , una manera de probar el diseño mediante la implementación del contrato.
Soporte de idiomas
Idiomas con soporte nativo
Los lenguajes que implementan la mayoría de las características de DbC de forma nativa incluyen:
- Ada 2012
- SPARK (mediante análisis estático de programas Ada )
- Hola
- Clojure
- Cobra
- C++ (desde C++26 ) [ 9 ]
- D [ 12 ]
- Dafny
- Eiffel
- Fortaleza
- Kotlin
- Mercurio
- Oxygene (anteriormente Chrome y Delphi Prism [ 13 ] )
- Extorsión (incluidos los contratos de orden superior, y haciendo hincapié en que las violaciones contractuales deben culpar a la parte culpable y deben hacerlo con una explicación precisa [ 14 ] )
- Sather
- Scala [ 15 ] [ 16 ]
- Vala
- Método de Desarrollo de Viena (VDM)
Además, la combinación de métodos estándar en el Common Lisp Object System tiene los calificadores de método :before, :aftery :aroundque permiten escribir contratos como métodos auxiliares, entre otros usos.
Véase también
Notas
- ↑ Meyer, Bertrand: Diseño por contrato , Informe técnico TR-EI-12/CO, Interactive Software Engineering Inc., 1986
- ↑ Meyer, Bertrand: Diseño por contrato , en Avances en ingeniería de software orientada a objetos , eds. D. Mandrioli y B. Meyer, Prentice Hall, 1991, págs. 1–50
- ↑ Meyer, Bertrand: " Aplicación del "Diseño por Contrato" ", en Computer (IEEE), 25, 10, octubre de 1992, pp. 40–51.
- ↑ "Registro en la Oficina de Patentes y Marcas de los Estados Unidos para "DISEÑO POR CONTRATO"" . Archivado del original el 21-12-2016 . Recuperado el 22-06-2009 .
- ↑ "Registro en la Oficina de Patentes y Marcas de los Estados Unidos para el diseño gráfico con las palabras "Diseño por contrato""" . Archivado del original el 21-12-2016 . Recuperado el 22-06-2009 .
- ↑ "Estado de la marca y recuperación de documentos - 78342277" . Recuperación de la solicitud y registro de marca de la USPTO .
- ↑ "Estado de la marca y recuperación de documentos - 78342308" . Recuperación de la solicitud y registro de marca de la USPTO .
- ^ Josué Berna; Timur Doumler; Andrzej Krzemieński (13 de febrero de 2025). "Contratos para C++" (PDF) . open-std.org . Grupo de Trabajo 22.
- 1 2 "Aserciones de contrato (desde C++26)" . cppreference.com . cppreference . Consultado el 9 de noviembre de 2025 .
- ↑ "Aserciones en código administrado" . Microsoft Developer Network . 15 de noviembre de 2016. Archivado del original el 22 de agosto de 2018.
- ↑ Documentación oficial de Python, instrucción assert
- ↑ Bright, Walter (1 de noviembre de 2014). "Lenguaje de programación D, programación por contrato" . Digital Mars . Consultado el 10 de noviembre de 2014 .
- ↑ Hodges, Nick. "Escriba código más limpio y de mayor calidad con contratos de clase en Delphi Prism" . Embarcadero Technologies. Archivado del original el 26 de abril de 2021. Recuperado el 20 de enero de 2016 .
- ↑ Findler, Felleisen Contratos para funciones de orden superior
- ↑ "Documentación de la biblioteca estándar de Scala - Aserciones" . EPFL . Consultado el 24 de mayo de 2019 .
- ↑ Tipado fuerte como otro "aplicamiento de contrato" en Scala, ver discusión en scala-lang.org/ .
Bibliografía
- Mitchell, Richard y McKim, Jim: Diseño por contrato: mediante ejemplos , Addison-Wesley, 2002
- Un wikilibro que describe DBC fielmente al modelo original.
- McNeile, Ashley: Un marco para la semántica de los contratos de comportamiento . Actas del Segundo Taller Internacional sobre Modelado del Comportamiento: Fundamentos y Aplicaciones (BM-FA '10). ACM, Nueva York, NY, EE. UU., 2010. Este artículo analiza nociones generalizadas de Contrato y Sustituibilidad .
Enlaces externos
- El poder del diseño por contrato (TM): una descripción general del diseño por contrato, con enlaces a recursos adicionales.
- Creación de software orientado a objetos sin errores: una introducción al Diseño por Contrato(TM). Material anterior sobre DbC.
- Ventajas e inconvenientes; implementación en RPS-Obix
- Uso de contratos de código para un código más seguro
- Diseño de software
- paradigmas de programación