En la programación de computadoras , específicamente en la programación orientada a objetos , un invariante de clase (o invariante de tipo ) es un invariante que se usa para restringir objetos de una clase . Los métodos de la clase deben preservar el invariante. La clase invariante restringe el estado almacenado en el objeto.
Los invariantes de clase se establecen durante la construcción y se mantienen constantemente entre llamadas a métodos públicos. El código dentro de las funciones puede romper invariantes siempre que se restauren antes de que finalice una función pública. Con la concurrencia, mantener el invariante en los métodos generalmente requiere que se establezca una sección crítica bloqueando el estado mediante un mutex.
Un objeto invariante, o representación invariante, es una construcción de programación informática que consta de un conjunto de propiedades invariantes que permanecen inalterables independientemente del estado del objeto . Esto asegura que el objeto siempre cumplirá las condiciones predefinidas y que , por lo tanto, los métodos siempre pueden hacer referencia al objeto sin el riesgo de hacer presunciones inexactas. La definición de invariantes de clase puede ayudar a los programadores y evaluadores a detectar más errores durante las pruebas de software .
Invariantes de clase y herencia
El efecto útil de las invariantes de clase en el software orientado a objetos se mejora en presencia de herencia. Los invariantes de clase se heredan, es decir, "los invariantes de todos los padres de una clase se aplican a la clase misma". [1]
La herencia puede permitir que las clases descendientes alteren los datos de implementación de las clases principales, por lo que sería posible que una clase descendiente cambiara el estado de las instancias de una manera que las invalidara desde el punto de vista de la clase principal. La preocupación por este tipo de descendientes que se comportan mal es una de las razones que dan los diseñadores de software orientado a objetos para favorecer la composición sobre la herencia (es decir, la herencia rompe la encapsulación). [2]
Sin embargo, debido a que los invariantes de clase se heredan, el invariante de clase para cualquier clase particular consiste en cualquier aserción invariante codificada inmediatamente en esa clase junto con todas las cláusulas invariantes heredadas de los padres de la clase. Esto significa que, aunque las clases descendientes pueden tener acceso a los datos de implementación de sus padres, la clase invariante puede evitar que manipulen esos datos de cualquier forma que produzca una instancia no válida en tiempo de ejecución.
Soporte de lenguaje de programación
Afirmaciones
Los lenguajes de programación comunes como Python, [3] JavaScript, [ cita requerida ] C ++ y Java admiten afirmaciones de forma predeterminada, que se pueden usar para definir invariantes de clase. Un patrón común para implementar invariantes en clases es que el constructor de la clase lance una excepción si no se satisface el invariante. Dado que los métodos conservan los invariantes, pueden asumir la validez del invariante y no necesitan verificarlo explícitamente.
Soporte nativo
El invariante de clase es un componente esencial del diseño por contrato . Por lo tanto, los lenguajes de programación que brindan soporte nativo completo para el diseño por contrato , como Rust , Eiffel , Ada y D , también brindarán soporte completo para invariantes de clase.
Soporte no nativo
Para C ++ , la biblioteca Loki proporciona un marco para verificar invariantes de clase, invariantes de datos estáticos y seguridad de excepciones.
Para Java, existe una herramienta más poderosa llamada Java Modeling Language que proporciona una forma más sólida de definir invariantes de clase.
Ejemplos de
Soporte nativo
D
El lenguaje de programación D tiene soporte nativo de invariantes de clase, así como otras técnicas de programación por contrato . A continuación, se muestra un ejemplo de la documentación oficial. [4]
class Date { int día ; hora int ; invariante () { aseverar ( día > = 1 && día <= 31 ); afirmar ( hora > = 0 && hora <= 23 ); } }
Eiffel
En Eiffel , la clase invariante aparece al final de la clase después de la palabra clave invariant
.
clase FECHAcrear hacercaracterística { NONE } : inicializaciónmake ( a_day : INTEGER ; a_hour : INTEGER ) - Inicializa `Current 'con` a_day' y `a_hour '. require valid_day : a_day > = 1 y a_day <= 31 valid_hour : a_hour > = 0 y a_hour <= 23 do day : = a_day hour : = a_hour asegurar day_set : day = a_day hour_set : hour = a_hour endcaracterística - Accesoday : INTEGER - Día del mes para 'Actual'hour : INTEGER - Hora del día para 'Actual'característica - Cambio de elementoset_day ( a_day : INTEGER ) - Establecer `day 'en` a_day' require valid_argument : a_day > = 1 y a_day <= 31 do day : = a_day asegura day_set : day = a_day endset_hour ( a_hour : INTEGER ) - Establecer `hour 'a` a_hour' require valid_argument : a_hour > = 0 y a_hour <= 23 do hour : = a_hour asegurar hour_set : hour = a_hour endinvariante día_valido : día > = 1 y día <= 31 hora_valida : hora > = 0 y hora <= 23 fin
Soporte no nativo
C ++
La biblioteca Loki (C ++) proporciona un marco escrito por Richard Sposato para verificar invariantes de clase, invariantes de datos estáticos y nivel de seguridad de excepciones .
Este es un ejemplo de cómo la clase puede usar Loki :: Checker para verificar que los invariantes permanezcan verdaderos después de que un objeto cambia. El ejemplo utiliza un objeto de punto geográfico para almacenar una ubicación en la Tierra como una coordenada de latitud y longitud.
Los invariantes de puntos geográficos son:
- la latitud no puede ser superior a 90 ° norte.
- la latitud no puede ser inferior a -90 ° sur.
- la longitud no puede ser superior a 180 ° este.
- la longitud no puede ser inferior a -180 ° oeste.
#include // Necesario para verificar invariantes de clase.#include class GeoPoint { public : GeoPoint ( grados de latitud , grados de longitud ); /// La función Mover moverá la ubicación de GeoPoint. void Move ( Degrees latitude_change , Degrees longitude_change ) { // El objeto verificador llama a IsValid en la entrada y salida de la función para probar que este // objeto GeoPoint es válido. El verificador también garantiza que la función GeoPoint :: Move // nunca se lanzará. CheckFor :: CheckForNoThrow checker ( esto , & IsValid ); latitude_ + = latitude_change ; if ( latitud_ > = 90.0 ) latitud_ = 90.0 ; si ( latitude_ <= -90,0 ) latitude_ = -90,0 ; longitude_ + = longitude_change ; while ( longitud_ > = 180.0 ) longitud_ - = 360.0 ; while ( longitud_ <= -180.0 ) longitud_ + = 360.0 ; } private : / ** @note CheckFor realiza una verificación de validez en muchas funciones para determinar si el código violó alguna invariante, si algún contenido cambió o si la función arrojó una excepción. * / using CheckFor = :: Loki :: CheckFor < const GeoPoint > ; /// Esta función comprueba todos los objetos invariantes. bool IsValid () const { aseverar ( esto ! = nullptr ); assert ( latitude_ > = -90,0 ); afirmar ( latitud_ <= 90.0 ); afirmar ( longitud_ > = -180.0 ); afirmar ( longitud_ <= 180.0 ); devuelve verdadero ; } Grados de latitud_ ; /// Positivo es el norte, negativo es /// Grados de longitud_ ; /// Positivo es este, /// }
Java
Este es un ejemplo de una clase invariante en el lenguaje de programación Java con Java Modeling Language . El invariante debe ser verdadero una vez finalizado el constructor y en la entrada y salida de todas las funciones miembro públicas. Las funciones de los miembros públicos deben definir una condición previa y una condición posterior para ayudar a garantizar que la clase sea invariante.
Fecha de clase pública { int / * @ spec_public @ * / día ; int / * @ spec_public @ * / hora ; / * @ día invariante> = 1 && día <= 31; @ * / // clase invariante / * @ invariante hora> = 0 && hora <= 23; @ * / // clase invariante / * @ @ requiere d> = 1 && d <= 31; @requiere h> = 0 && h <= 23; @ * / public Date ( int d , int h ) { // día del constructor = d ; hora = h ; } / * @ @ requiere d> = 1 && d <= 31; @asegura el día == d; @ * / public void setDay ( int d ) { día = d ; } / * @ @ requiere h> = 0 && h <= 23; @asegura la hora == h; @ * / public void setHour ( int h ) { hora = h ; } }
Referencias
- ^ Meyer, Bertrand. Construcción de software orientado a objetos , segunda edición, Prentice Hall, 1997, p. 570.
- ^ E. Gamma, R. Helm, R. Johnson y J. Vlissides. Patrones de diseño: elementos de software orientado a objetos reutilizable . Addison-Wesley, Reading, Massachusetts, 1995., pág. 20.
- ^ Documentos oficiales de Python, declaración de afirmación
- ^ "Programación por contrato - Lenguaje de programación D" . dlang.org . Consultado el 29 de octubre de 2020 .