Programación en Ada/Tipos abstractos de datos

← Compilación separada y dependiente Tipos abstractos de datos Tipos limitados →


Tipos abstractos de datos (tipos privados)

editar

Una de las principales contribuciones de los lenguajes de alto nivel es que el programador no tiene que preocuparse de cómo se representan físicamente los datos en el computador. De esta idea surge el concepto de tipo de datos. Una extensión del mismo es el tipo abstracto de datos. Su implementación es de nuevo desconocida para el programador, esta vez no porque desconozca la arquitectura del computador subyacente, sino porque es encapsulado en un módulo que no permite el acceso directo a los detalles de su implementación. En su lugar, se proporciona al programador operaciones sobre el tipo que son invocaciones a entradas del módulo que lo encapsula.

Por ejemplo, consideremos la utilización de un tipo abstracto de datos que represente a un número complejo:

package Números_complejos is
  type TComplejo is
    record
      Real, Imag: Float;
    end record;
  I: constant TComplejo := (0.0, 1.0);
  function "+" (X, Y: TComplejo) return TComplejo;
  function "-" (X, Y: TComplejo) return TComplejo;
  function "*" (X, Y: TComplejo) return TComplejo;
  function "/" (X, Y: TComplejo) return TComplejo;
end Números_complejos;

De este modo, el usuario debe conocer los detalles de la implementación y sabe que se utiliza una representación cartesiana. Además, el usuario está obligado a hacer uso de la representación.

Para impedir el uso del conocimiento de la representación con vistas, por ejemplo, a poder cambiar ésta posteriormente, se puede hacer uso de los tipos privados definiéndolos mediante la palabra reservada private:

package Números_complejos is
  -- Parte visible.
  type TComplejo is private;  -- Tipo privado.
  I: constant TComplejo;  -- No se puede asignar valor todavía.
  function "+" (X, Y: TComplejo) return TComplejo;
  function "-" (X, Y: TComplejo) return TComplejo;
  function "*" (X, Y: TComplejo) return TComplejo;
  function "/" (X, Y: TComplejo) return TComplejo;
  function Construir_complejo (R, I: Float) return TComplejo;
  function Parte_imaginaria (X: TComplejo) return Float;
  function Parte_real (X: TComplejo) return Float;
private
  -- Parte oculta.
  type TComplejo is
    record
      Real, Imag: Float;
    end record;
  I: constant TComplejo := (0.0, 1.0);
end Números_complejos;

Ahora, se ha definido TComplejo como tipo privado y se resguardan los detalles de su implementación en la parte no visible del paquete después de la palabra reservada private y hasta el fin de la especificación del paquete. En la parte visible (desde el comienzo de la especificación hasta private), se da la información disponible fuera del paquete.

Las únicas operaciones disponibles son la asignación, la igualdad y la desigualdad, aparte de las añadidas en el paquete.

Nótese que el valor de I no se puede dar pues no se conocen todavía los detalles de la implementación, se declara como constante y se le asigna después un valor en la parte privada.

Las funciones Construir_complejo, Parte_imaginaria y Parte_real son ahora necesarias pues el usuario ya no conoce la estructura del tipo TComplejo y se necesita realizar dicha interfaz para poder manejar objetos del tipo privado.

El cuerpo se podría implementar de la siguiente manera:

package body Números_complejos is
  function "+" (X, Y: Tcomplejo) return TComplejo is
  begin
    return (X.Real + Y.Real, X.Imag + Y.Imag);
  end "+";
  -- ... "-", "* y "/" similarmente.
  function Construir_complejo (R, I: Float) return TComplejo is
  begin
    return (R, I);
  end Construir_complejo;
  function Parte_real (X: TComplejo) return Float is
  begin
    return X.Real;
  end Parte_real;
  -- ... Parte_imaginaria análogamente.
end Números_complejos;

Y podría ser utilizado transparentemente, por ejemplo, dentro de un bloque como:

declare
  use Números_complejos;
  C1, C2: TComplejo;
  R1, R2: Float;
begin
  C1 := Construir_complejo (1.5, -6.0);
  C2 := C1 + I;
  R := Parte_real (C2) + 8.0;
end;

Si ahora se quisiera cambiar la implementación del tipo TComplejo y representarlo en forma polar, no sería necesario cambiar la parte visible de la especificación, por lo que todas las unidades que utilicen dicho paquete no tienen la necesidad de actualizarse. La interfaz exportada no ha cambiado y, por tanto, los programas que la utilizarán pueden seguir haciéndolo. Por ejemplo, ahora se podría representar en la parte privada de la especificación del paquete como:

-- ...
private
  Pi: constant := 3.1416;
  type TComplejo is
    record
      R: Float;
      Theta: Float range 0.0 .. 2*Pi;
    end recod;
  I: constant TComplejo := (1.0, 0.5*Pi);
end Números_complejos;

Lo único que se necesitaría sería reescribir el cuerpo del paquete y recompilarlo.

Enlaces externos

editar

Manual de referencia de Ada

editar