c# .net c#-4.0 code-contracts design-by-contract

c# - Diseño por contratos y constructores



.net c#-4.0 (5)

Estoy completamente en desacuerdo con la respuesta de Thomas. Siempre y cuando esté haciendo elecciones en la implementación de ArrayList() , debe tener un contrato que documente estas opciones.

Aquí, está haciendo la elección de llamar al constructor principal con el argumento 32. Hay muchas otras cosas que podría haber decidido hacer (no solo con respecto a la elección del tamaño predeterminado). Al otorgarle un contrato a ArrayList() que es casi idéntico al de los documentos ArrayList(int) , usted decidió no hacer la mayoría de las tonterías que podría haber hecho en lugar de llamarlo directamente.

La respuesta "llama al constructor principal, así que deja que el contrato del constructor principal haga el trabajo" ignora por completo el hecho de que el contrato está ahí para salvarte de tener que mirar la implementación. Para una estrategia de verificación basada en la comprobación de aserciones en tiempo de ejecución, la desventaja de escribir contratos incluso para constructores / métodos cortos que llaman casi directamente a otro constructor / método es que terminas revisando las cosas dos veces. Sí, parece redundante, pero la verificación de aserciones en tiempo de ejecución es solo una estrategia de verificación, y los principios de DbC son independientes de ella. El principio es: si se puede llamar, necesita un contrato para documentar lo que hace.

Estoy implementando mi propia ArrayList para fines escolares, pero para darle más sabor a las cosas, estoy tratando de usar C # 4.0 Code Contracts. Todo estaba bien hasta que necesité agregar contratos a los constructores. ¿Debo agregar Contract.Ensures () en el constructor de parámetros vacío?

public ArrayList(int capacity) { Contract.Requires(capacity > 0); Contract.Ensures(Size == capacity); _array = new T[capacity]; } public ArrayList() : this(32) { Contract.Ensures(Size == 32); }

Yo diría que sí, que cada método debe tener un contrato bien definido. Por otro lado, ¿por qué ponerlo si solo está delegando trabajo al constructor "principal"? Lógicamente, no necesitaría hacerlo.

El único punto que veo en el que sería útil definir explícitamente el contrato en ambos constructores es si en el futuro tenemos soporte de Intelisense para los contratos. Si eso sucediera, sería útil ser explícito sobre los contratos que tiene cada método, como aparecería en Intelisense.

Además, ¿hay libros que profundicen en los principios y el uso del Diseño por Contratos? Una cosa es tener conocimiento de la sintaxis de cómo usar Contracts en un idioma (C #, en este caso), otro es saber cómo y cuándo usarlo. Leí varios tutoriales y el artículo C # in Depth de Jon Skeet al respecto, pero me gustaría ir un poco más profundo si es posible.

Gracias


Umh, no entiendo completamente por qué pones el ''Seguro'' también en el teclado predeterminado. Debido a que llama al administrador principal, que ya implementa el contrato completo, el administrador predeterminado también lo hace, por definición. Entonces esta es una redundancia lógica, y por lo tanto un gran ''No''. Tal vez podría tener implicaciones pragmáticas, como dices, no conoces los contratos de código tan buenos ...

En cuanto a la literatura, las mejores fuentes son:

HTH! Thomas


El diseño por contrato proviene de las raíces matemáticas de la programación funcional: Precondiciones y Postcondiciones .

Realmente no necesitas un libro, es como máximo un capítulo de un título en Informática (la mayoría enseñará el concepto). La premisa básica es que escriba las condiciones previas que espera la función y la salida que producirá dados los parámetros correctos. No se espera que la función funcione con parámetros iniciales incorrectos. Lo mismo puede decirse de un algoritmo: es infalible, es decir, garantiza el resultado esperado.

Así es como me lo enseñaron en el grado que estoy estudiando, aunque puede haber mejores definiciones. El artículo de Wikipedia sobre Diseño por contrato está escrito con una orientación OO, pero las condiciones previas y posteriores son independientes del idioma.


El código de cliente (usando Contratos de código) que usa ArrayList no sabrá que el constructor vacío Ensure s ese Size == 32 menos que usted lo indique explícitamente usando Ensure .

Así por ejemplo):

var x = new ArrayList(); Contract.Assert(x.Size == 32)

le dará la advertencia "afirmar no probado".

Necesita declarar explícitamente todos los contratos; el código contrata rewriter / static checker para que no "vea" un método para ver las implicaciones; consulte mi respuesta a la pregunta relacionada "¿Tenemos que especificar sentencias Contract.Requires (...) de forma redundante al delegar métodos?"


Recomiendo leer Construcción de software orientado a objetos, segunda edición , o tal vez Touch of Class , ambos de Bertrand Meyer. Alternativamente, podría leer el artículo de 1992 Aplicando "Diseño por contrato" del mismo autor.

Para resumir:

  • El invariante de clase debe mantenerse después de que el constructor (cualquiera de ellos) finalice, y antes y después de que se ejecute cualquier método público de la clase.
  • Las condiciones previas y postcondiciones del método son condiciones adicionales que deben mantenerse al ingresar y salir de cualquier método público, junto con el invariante.

Entonces en tu caso, concéntrate en lo invariante. Produzca un objeto correcto (uno que satisfaga el invariante de clase), sin importar qué constructor invoque.

En esta respuesta relacionada , discutí temas similares, incluido un ejemplo.