Un des inconvénients des organigrammes est leur manque de définition des données. Les organigrammes documentent le séquençage logique et les chemins de décision, mais pas les données utilisées par le programme.
Un programme complet nécessite une définition claire à la fois de l'algorithme (logique du programme) et des données sur lesquelles il agit. L'un des objectifs de la programmation orientée objet est de définir ensemble les données et le comportement. Par exemple, si votre programme a besoin d'une structure de données Stack cette structure de données Stack doit se comporter d'une manière bien définie et cohérente.
Une Stack peut être implémentée en utilisant un tableau ou elle peut être implémentée en utilisant une liste liée, mais les comportements abstraits seront les mêmes. Par exemple, les comportements courants d'une pile sont :
- Push - Placer un élément au sommet de la pile
- Pop - Retirer l'élément au sommet de la pile
- Top - Afficher l'élément au sommet de la pile sans modifier la pile
- Clear - Vider. tous les éléments de la pile
- Is_Empty - Indique quand une pile est vide
- Is_Full - Indique quand une pile bornée est pleine
Bien qu'un organigramme puisse être créé pour chacune de ces six opérations, un programme ne peut pas être créé en sachant simplement ce que chaque opération doit faire. La pile sera-t-elle implémentée à l'aide d'un tableau, créant communément une pile bornée avec une capacité maximale fixe, ou sera-t-elle implémentée à l'aide d'une liste chaînée créant une pile non bornée dont la capacité n'est limitée que par la quantité de mémoire disponible sur l'ordinateur ?
Comment un programme saura-t-il quel type de pile implémenter simplement en lisant un organigramme ?
De nombreux langages résolvent ce genre de problème en permettant au programmeur de créer des bibliothèques de types de données génériques. Les génériques appliquent un algorithme et sont spécialisés en étant paramétrés avec le type de données qu'ils doivent contenir, et éventuellement le nombre maximum d'éléments qu'ils doivent gérer.
Voici deux exemples de paquets de piles génériques 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 spécification du package définit le paramètre générique utilisé par le package. Dans ce cas, le seul paramètre est un type de données. Il peut être n'importe quel type de données.
La spécification définit l'API ou l'interface publique du type de données Stack. La partie privée de la spécification du package définit le type de données privé utilisé pour implémenter la pile bornée. Le type Stack est constitué d'un Size, d'un tableau contenant les données, d'un index dans le tableau indiquant le sommet de la pile, et d'un décompte des éléments actuellement sur la pile.
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 accès en Ada est similaire à une référence en Java ou C++.
Notez que la procédure Push pour la pile non bornée n'a pas de pré-condition alors que la procédure Push pour la pile bornée a une pré-condition. Vous attendez-vous à ce que l'outil automatisé soit capable de deviner la différence entre les pré-conditions des procédures à partir d'un organigramme ?
.