2010-05-04 10 views
10

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?Diseño por contratos y constructores

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í, 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 algún libro que profundice en los principios y el uso de Design by Contracts? Una cosa es tener conocimiento de la sintaxis de cómo usar Contracts en un idioma (C#, en este caso), otra 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

+0

relacionado: http://stackoverflow.com/questions/2539497/code-contracts-do-we-have-to-specify-contract-requires-statements-redundant/2626997 – porges

+0

Puede deshacerse del "Contrato". Requiere (capacidad> 0); " si haces que el controlador haga una uint frente a un int. Intento usar Contracts como último recurso, donde el lenguaje limita tu capacidad de permitir que el siguiente desarrollador sepa lo que estabas pensando cuando creaste el código en primer lugar. Si decide mantener el contrato, escribiría "Contract.Requires (capacity> = 0);" para uno siempre debería ser capaz de construir una estructura de datos vacía, y luego tener la opción de agregar objetos más tarde. –

+0

"... en el futuro tenemos soporte Intelisense para contratos". ¡El futuro es ahora! http://visualstudiogallery.msdn.microsoft.com/1ec7db13-3363-46c9-851f-1ce455f66970 –

Respuesta

5

Estoy completamente en desacuerdo con la respuesta de Thomas. Siempre que elija en la implementación de ArrayList(), debe tener un contrato que documente estas opciones.

Aquí tiene la opció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). Otorgando un contrato al ArrayList() que es casi idéntico al de los documentos ArrayList(int) que decidió no hacer la mayor parte 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.

+1

Sí; y si usa el verificador estático le dirá esto :) – porges

0

UMH que no entienden completamente por qué se pone los 'Asegura también en el c'tor defecto. 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 usted dice - no se sabe que los buenos Code Contracts ...

En cuanto a la literatura - las mejores fuentes son:

HTH! Thomas

+0

Suponiendo que un día Intelisense sea totalmente compatible con Contratos de código (o suponiendo que a partir de hoy, veremos la documentación de contratos XML), declarando explícitamente que el contrato del constructor no args tendría el beneficio de permitir que el cliente conozca el contrato. Si lo dejas vacío, no puedes. Y esa es la razón por la que creé esta publicación. –

+1

Entiendo el razonamiento, y tal vez esto se hará realidad algún día ... Pero haciendo algo. porque posiblemente podría haber beneficios en el futuro resultó ser una de las peores (y más caras) cosas en el desarrollo de software. ¡Siempre haz lo más simple que hace el trabajo! –

+0

Sí, tienes un punto. ¿Qué sucede si Intelisense admite esta función a partir de hoy? ¿Tendría sentido poner ese contrato en el constructor no args? –

1

El código de cliente (usando Code Contracts) que utiliza ArrayList no sabrá que el constructor vacío Ensure s que Size == 32 a menos que se dice explícitamente lo que el uso de un Ensure.

Así que (por ejemplo):

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

le dará la advertencia "no afirma probado".

Debe indicar explícitamente todos los contratos; el código contrae regrabadora/de electricidad estática no va a "mirar a través de" un método para ver las implicaciones — ver my answer to the related question "Do we have to specify Contract.Requires(…) statements redundantly in delegating methods?"

0

Diseño por contrato proviene de las raíces matemáticas de la programación funcional: Preconditions y Postconditions.

Realmente no necesita 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 actualmente, 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.

+0

Sí, mi punto es que en el diseño de OO en papel no tiene mucho que aprender también. Aprende sobre clases, herencia y encapsulación, y parece que no hay mucho más que aprender. Pero en la práctica, hay toneladas de libros sobre cómo hacer buenos programas OO. A veces conocer la sintaxis y la "teoría" no es suficiente, solo eso. –

+0

Esos son probablemente los libros Wrox y apress que tratan sobre la practicidad :) Se enseña mucho más profundidad en un grado (decente) –

+0

No tengo un título, entonces ¿qué debo hacer? –

1

Recomiendo leer Object Oriented Software Construction, 2nd Edition, o tal vez Touch of Class, ambos de Bertrand Meyer. Alternativamente, podría leer el artículo de 1992 Applying "Design by Contract" del mismo autor.

En resumen:

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

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

En este related answer discutí temas similares, incluyendo un ejemplo.

Cuestiones relacionadas