Uno svantaggio dei diagrammi di flusso è la loro mancanza di definizione dei dati. I diagrammi di flusso documentano la sequenza logica e i percorsi decisionali, ma non i dati usati dal programma.
Un programma completo richiede una chiara definizione sia dell'algoritmo (logica del programma) che dei dati su cui agisce. Uno degli obiettivi della programmazione orientata agli oggetti è quello di definire insieme dati e comportamento. Per esempio, se il vostro programma ha bisogno di una struttura dati Stack, questa struttura dati deve comportarsi in un modo ben definito e coerente.
Una Stack può essere implementata usando un array o può essere implementata usando una lista collegata, ma i comportamenti astratti saranno gli stessi. Per esempio, i comportamenti comuni di uno stack sono:
- Push - Mettere un elemento in cima allo stack
- Pop - Rimuovere l'elemento in cima allo stack
- Top - Visualizzare l'elemento in cima allo stack senza cambiare lo stack
- Clear - Svuotare tutti gli elementi dallo stack
- Is_Empty - Indica quando uno stack è vuoto
- Is_Full - Indica quando uno stack delimitato è pieno
Mentre un diagramma di flusso può essere creato per ciascuna di queste sei operazioni, un programma non può essere creato solo sapendo cosa deve fare ogni operazione. Lo stack sarà implementato usando un array, creando comunemente uno stack delimitato con una capacità massima fissa, o sarà implementato usando una lista collegata creando uno stack non delimitato la cui capacità è limitata solo dalla quantità di memoria disponibile sul computer?
Come farà un programma a sapere che tipo di stack implementare semplicemente leggendo un diagramma di flusso?
Molti linguaggi risolvono questo tipo di problema permettendo al programmatore di creare librerie di tipi di dati generici. I generici applicano un algoritmo e sono specializzati essendo parametrizzati con il tipo di dati che devono contenere, ed eventualmente il numero massimo di elementi che devono gestire.
Ecco due esempi di pacchetti di stack generici in 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 specifica del pacchetto definisce il parametro generico usato dal pacchetto. In questo caso l'unico parametro è un tipo di dati. Può essere qualsiasi tipo di dati.
La specifica definisce l'API o interfaccia pubblica al tipo di dati Stack. La parte privata della specifica del pacchetto definisce il tipo di dati privato usato per implementare lo stack delimitato. Il tipo Stack consiste di una Dimensione, un array contenente i dati, un indice nell'array che indica la cima dello stack, e un conteggio degli elementi attualmente sullo stack.
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 accesso in Ada è simile a un riferimento in Java o C++.
Nota che la procedura Push per lo stack non delimitato non ha alcuna pre-condizione mentre la procedura Push per lo stack delimitato ha una pre-condizione. Vi aspettate che lo strumento automatizzato sia in grado di capire la differenza tra le pre-condizioni delle procedure da un diagramma di flusso?