Invariantes de clase

Un objeto invariante, o una representación invariante, es un constructo de programación que consiste en una colección de propiedades invariantes que se mantienen intactas sea cual sea el estado del objeto. Esto garantiza que el objeto cumplirá siempre con ciertas condiciones predefinidas, y que los métodos pueden, en consecuencia, hacer referencia al objeto sin el riesgo de hacer presunciones imprecisas.

En programación, concretamente en programación orientada a objetos, las «invariantes de clase» son invariantes usadas para restringir los objetos de una clase. Los métodos de la clase deben respetar la invariante. La invariante de la clase restringe el estado guardado en el objeto.

Las invariantes de la clase se establecen durante su construcción y se mantienen entre las llamadas a métodos públicos. Es posible romper la invariabilidad de la clase entre llamadas a métodos privados, pero no es aconsejable.

Definir invariantes de clase puede ayudar a los programadores y controladores de calidad a localizar más errores de software durante las pruebas de software.

Clases invariantes y herencia

editar

Las ventajas de las invariantes de son más evidentes en presencia de herencia, ya que las invariantes de clase se heredan, es decir, «las invariantes de todas las clases superiores se aplican a la clase actual»[1]

La herencia permite que las clases derivadas alteren la implementación de sus clases base, por lo que una clase derivada podría cambiar el estado de sus instancias de manera incorrecta desde el punto de vista de su clase base. La preocupación por este comportamiento no deseado lleva a algunos diseñadores de software a favorecer la composición sobre la herencia (por ejemplo, «La herencia rompe la encapsulación»).[2]

Sin embargo, debido a la herencia, las invariantes de una clase cualquiera consisten en la suma de todas sus aserciones invariantes con las invariantes heredadas de su clase base. Esto significa que aunque las clases derivadas pueden acceder a los datos de implementación de su clase base, las invariantes de la clase base evitan que las derivadas manipulen estos datos de manera que produzcan una instancia incorrecta en tiempo de ejecución.

Herramientas de programación de soporte a invariantes

editar

Aserciones

editar

Algunos de los lenguajes de programación más comunes, como C++ y Java permiten el uso de aserciones por defecto, estas pueden usarse para definir invariantes de clase.

Un patrón de diseño común para implementar las invariantes de clase en el constructor del objeto es lanzar una excepción si la invariante no se cumple. Dado que los métodos respetan las invariantes, pueden asumir su validez y no necesitan verificarlas explícitamente.

Soporte nativo

editar

Las invariantes de clase son un componente esencial para el patrón de diseño por contrato. Por ello, los lenguajes que proporcionan un soporte completo al diseño por contrato, como Ada, Eiffel o D (lenguaje de programación), también proporcionan un soporte completo a las invariantes de clase.

Soporte no nativo

editar

Java dispone de una herramienta más potente llamada Java Modeling Language que ofrece un método más robusto para definir invariantes de clase.

Ejemplos

editar

Soporte nativo

editar

El lenguaje de programación D dispone de soporte nativo a las invariantes de clase, además de otras técnicas de diseño por contrato.

class Fecha {
  int dia;
  int hora;

  invariant() {
    assert(1 <= dia && dia <= 31);
    assert(0 <= hora && hora < 24);
  }
}

Eiffel

editar

En Eiffel, las invariantes de clase se detalla al final de la clase precedidas por la palabra clave invariant.

class
	FECHA

create
	make

feature {NONE} -- Initialization

	make (a_dia: INTEGER; a_hora: INTEGER)
			-- Inicializa el objeto con `a_dia' y `a_hora'.
		require
			dia_valido: 1 <= a_dia and a_dia <= 31
			hora_valida: 0 <= a_hora and a_hora <= 23
		do
			dia := a_dia
			hora := a_hora
		ensure
			dia_establecido: dia = a_dia
			hora_establecida: hora = a_hora
		end

feature -- Acceso

	dia: INTEGER
		-- dia del mes

	hora: INTEGER
		-- hora del dia

feature -- Cambio de valores

	set_dia (a_dia: INTEGER)
			-- Establece `dia' a `a_dia'
		require
			argumento_valido: 1 <= a_dia and a_dia <= 31
		do
			dia := a_dia
		ensure
			dia_establecido: dia = a_dia
		end

	set_hora (a_hora: INTEGER)
			-- Establece `hora' a `a_hora'
		require
			argumento_valido: 0 <= a_hora and a_hora <= 23
		do
			hora := a_hora
		ensure
			hora_establecida: hora = a_hora
		end

invariant
	dia_valido: 1 <= dia and dia <= 31
	hora_valida: 0 <= hora and hora <= 23
end

Soporte no nativo

editar

Este es un ejemplo de invariantes de clase en Java con Java Modeling Language. Las invariantes deben cumplirse después de que finalice el constructor del objeto y al entrar y salir de todos los métodos públicos. Los métodos deben definir precondiciones y postcondiciones para ayudar a cumplir la invariabilidad de la clase.

public class Fecha {
    int /*@spec_public@*/ dia;
    int /*@spec_public@*/ hora;

    /*@invariant 1 <= dia && dia <= 31; @*/ //invariante de clase
    /*@invariant 0 <= hora && hora < 24; @*/ //invariante de clase

    /*@
    @requires 1 <= d && d <= 31;
    @requires 0 <= h && h < 24;
    @*/
    public Fecha(int d, int h) { // constructor
        dia = d;
        hora = h;
    }

    /*@
    @requires 1 <= d && d <= 31;
    @ensures dia == d;
    @*/
    public void setdia(int d) {
        dia = d;
    }

    /*@
    @requires 0 <= h && h < 24;
    @ensures hora == h;
    @*/
    public void sethora(int h) {
        hora = h;
    }
}

Véase también

editar

Referencias

editar
  1. Meyer, Bertrand. Object-Oriented Software Construction, segunda edición, Prentice Hall, 1997, p. 570.
  2. E. Gamma, R. Helm, R. Johnson, y J. Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, Reading, Massachusetts, 1995., p. 20.