Un inconveniente de los diagramas de flujo es su falta de definición de los datos. Los diagramas de flujo documentan la secuencia lógica y las rutas de decisión, pero no los datos utilizados por el programa.
Un programa completo requiere una definición clara tanto del algoritmo (lógica del programa) como de los datos sobre los que actúa. Uno de los objetivos de la Programación Orientada a Objetos es definir los datos y el comportamiento juntos. Por ejemplo, si su programa necesita una estructura de datos de Pila, esa estructura de datos de Pila debe comportarse de una manera bien definida y consistente.
Una Pila puede ser implementada usando un array o puede ser implementada usando una lista enlazada, pero los comportamientos abstractos serán los mismos. Por ejemplo, los comportamientos comunes de una pila son:
- Push - Poner un elemento en la parte superior de la pila
- Pop - Eliminar el elemento en la parte superior de la pila
- Top - Ver el elemento en la parte superior de la pila sin cambiar la pila
- Borrar - Vaciar todos los elementos de la pila
- Is_Empty - Indica cuando una pila está vacía
- Is_Full - Indica cuando una pila delimitada está llena
Si bien se puede crear un diagrama de flujo para cada una de estas seis operaciones, no se puede crear un programa sólo sabiendo lo que debe hacer cada operación. Se implementará la pila usando un array, creando comúnmente una pila acotada con una capacidad máxima fija, o se implementará usando una lista enlazada creando una pila no acotada cuya capacidad está limitada sólo por la cantidad de memoria disponible en el ordenador?
¿Cómo va a saber un programa qué tipo de pila implementar simplemente leyendo un diagrama de flujo?
Muchos lenguajes resuelven este tipo de problema permitiendo al programador crear bibliotecas de tipos de datos genéricos. Los genéricos aplican un algoritmo y se especializan al ser parametrizados con el tipo de datos que deben contener, y posiblemente el número máximo de elementos que deben manejar.
Aquí hay dos ejemplos de paquetes de pilas genéricas en Ada. The first example is a generic bounded stack. The second example is a generic unbounded stack.
- Generic
- type Element_Type is private;
- package Generic_Bounded_Stack is
- type Stack(Size : Positive) is tagged private;
- function Is_Empty(Item : in Stack) return Boolean;
- function Is_Full(Item : in Stack) return Boolean;
- function Count(Item : in Stack) return Natural;
- function Top(Item : in Stack) return Element_Type with
- Pre => not Item.Is_Empty;
- procedure Push(Item : in out Stack; Value : in Element_Type) with
- Pre => not Item.Is_Full,
- Post => (Item.Count = Item'Old.Count + 1 and
- Item.Top = Value);
- procedure Pop(Item : in out Stack; Value : out Element_Type) with
- Pre => not Item.Is_Empty,
- Post => (Item.Count = Item'Old.Count - 1 and
- Value = Item'Old.Top);
- private
- type Buf_T is array(Natural range <>) of Element_Type;
- type Stack(Size : Positive) is tagged record
- Buf : Buf_T(1..Size);
- Idx : Positive := 1;
- Tally : Natural := 0;
- end record;
- end Generic_Bounded_Stack;
The file shown above is the package specification for a generic unbounded stack. La especificación del paquete define el parámetro genérico utilizado por el paquete. En este caso el único parámetro es un tipo de datos. Puede ser cualquier tipo de datos.
La especificación define la API o interfaz pública del tipo de datos Stack. La parte privada de la especificación del paquete define el tipo de datos privado utilizado para implementar la pila acotada. El tipo Pila consta de un Tamaño, una matriz que contiene los datos, un índice dentro de la matriz que indica la parte superior de la pila y un recuento de los elementos que se encuentran actualmente en la pila.
The package specification for an unbounded stack is:
- generic
- type Element_Type is private;
- package Generic_Unbounded_Stack is
- type Stack is tagged private;
- function Is_Empty(Item : Stack) return Boolean;
- function Count(Item : Stack) return Natural;
- procedure Push(Item : in out Stack; Value : Element_Type) with
- Post => (Item.Count = Item'Old.Count + 1 and
- Item.Top = Value);
- procedure Pop(Item : in out Stack; Value : out Element_Type) with
- Pre => not Item.Is_Empty,
- Post => (Item.Count = Item'Old.Count - 1 and
- Value = Item'Old.Top);
- function Top(Item : in Stack) return Element_Type with
- Pre => not Item.Is_Empty;
- procedure Remove(Item : in out Stack) with
- Post => Item.Is_Empty;
- private
- type Node;
- type Node_Access is access Node;
- type Node is record
- Value : Element_Type;
- Next : Node_Access;
- end record;
- type Stack is tagged record
- Tally : Natural := 0;
- Head : Node_Access;
- end record;
- end Generic_Unbounded_Stack;
Note that the only difference between the public API for the unbounded stack is that it does not have a function Is_Full. The private part is very different. There is no data member named Size because the size is variable. The Stack type defined in the private part only contains a Tally holding the number of elements in the stack and a variable named Head which holds an access to the list containing the elements. Un acceso en Ada es similar a una referencia en Java o C++.
Note que el procedimiento Push para la pila no limitada no tiene ninguna precondición mientras que el procedimiento Push para la pila limitada tiene una precondición. ¿Espera que la herramienta automatizada sea capaz de adivinar la diferencia en las precondiciones de los procedimientos a partir de un diagrama de flujo?