Sort
Cet exemple montre plusieurs versions successives d'un logiciel. Ces différentes
versions permettent d'illustrer certains concepts parmi lesquels
-
des modèles de conception : Singleton, Fabrique, Décorateur,
Observateur/Observable
-
l'utilisation des threads
Version 1.0
Il s'agit d'écrire une application qui permette d'exécuter
un tri sur un jeu de données et de visualiser les différentes
actions de ce tri sur la sortie standard.
L'interface Sort décrit un tri comme étant une classe
implémentant une méthode doSort(SortableData).
L'interface SortableData décrit une collection triable.
Une telle collection doit implémenter quatre méthodes
-
int length() qui renvoie le nombre de données à
trier,
-
Object access(int i) qui renvoie le ième objet de la collection,
-
int compare(int i, int j) qui compare les ième et
jème objets de la collection et renvoie un entier négatif
si le ième objet est le plus petit, positif s'il est le plus
grand et 0 si les deux objets sont égaux,
-
void swap(int i, int j) qui échange les ième
et jème objets à l'intérieur de la collection
(par effet de bord).
QuickSort et InsertionSort sont deux implémentations
de Sort. Ce sont toutes les deux des singletons, puiqu'il n'y
a aucune raison de créer deux instances d'un même algorithme
de tri.
SortableArray est une implémentation de SortableData,
créée à partir d'un tableau. Le tableau passé
en paramètre du constructeur sera trié par l'application
de Sort.doSort(SortableData) au SortableArray obtenu.
SortableDataTracer et SortableDataStatistic sont
deux décorateurs de SortableData qui permettent respectivement
l'affichage des actions effectuées par le tri sur la sortie standard,
et le calcul de statistiques sur le tri (nombre d'accès, de comparaisons
et d'échanges).
Version 2.0
L'amélioration consiste à doubler la visualisation sur la
sortie standard par une visualisation graphique.
Les modifications de InsertionSort et QuickSort se situent
uniquement au niveau du main(String []) pour prendre en compte
le passage de SortableDataTracer en Observable, avec
comme observateurs un AsciiTracer et un WinTracer. De
plus, les main(String []) se terminent par l'instruction System.exit(0);
pour quitter l'application. Sinon, celle-ci continue car le fait d'utiliser
un composant graphique a lancé un thread pour la gestion des événements.
La classe SortableDataTracer a été profondemment
modifiée puisqu'elle étend désormais java.util.Observable
et ne fait plus d'affichage elle-même, mais prévient des traceurs
(implémentant java.util.Observer).
Les classes InsertionSort, QuickSort, Sort,
SortableArray,
SortableData,
SortableDataStatistic
n'ont pas été modifiées.
Les classes AsciiTracer et WinTracer implémentent
toutes les deux un Observer permettant une double visualisation
du déroulement du tri par une interface graphique (WinTracer)
et sur la sortie standard (AsciiTracer).
Version 2.1
Une légère amélioration peut être apportée
à WinTracer en remplaçant la boucle de temporisation
for (int i = 0; i < slider.getValue() * 100000;
i++)
;
par les instructions
try {
Thread.currentThread().sleep(50
* slider.getValue());
} catch (InterruptedException e) {
}
qui rende la temporisation indépendante de la machine utilisée.
Version 3.0
Cette version a pour but de ne plus obliger à écrire un main(String
[]) pour chaque nouveau tri. Le nom du tri sera passé comme
paramètre de la ligne de commande.
Les modifications de InsertionSort et QuickSort se situent
uniquement dans l'ajout d'une méthode de classe getInstance()
qui renvoie une instance de la classe. Les méthodes main(String
[]) de ces deux classes ne sont plus indispensables, mais restent
utilisables.
Les classes AsciiTracer, Sort,
SortableArray,
SortableData,
SortableDataStatistic,
SortableDataTracer,
WinTracer
n'ont pas été modifiées.
Les classes SortFactory, SortNotFoundException et
TestSort ont été rajoutées.
La classe SortFactory est une fabrique qui ne contient que
des méthodes de classe. Elle renvoie une instance d'un tri, soit
par l'appel d'une méthode spécifique au tri désiré,
soit par le passage d'un mot clé. Si le mot-clé ne convient
pas, une exception SortNotFoundException est levée. A noter
qu'il s'agit à nouveau d'un singleton. Mais cette fois, contrairement
à QuickSort et InsertionSort, aucune instance n'est créée.
L'unicité vient du fait que la classe ne possède aucune méthode
d'instance.
La classe TestSort sert de support à la méthode
main(String []) utilisée pour lancer l'application.
Version 3.1
Plutôt que d'utiliser une fabrique, on utilise dans cette version
le chargement dynamique de classe. Donc au lieu de passer un mot-clé
pour désigner un tri, on passe directement le nom de la classe qui
l'implémente.
Les classes SortFactory et SortNotFoundException sont
désormais inutiles.
La méthode main(String []) de la classe TestSort
a été modifiée pour effectuer le chargement dynamique
plutôt que d'utiliser SortFactory.
Version 3.99
Il s'agit en fait d'une version préliminaire à la version
4.0. Il est possible dans cette version d'exécuter plusieurs méthodes
de tris sur un même jeu de données dans un seul appel. Toutefois,
ces tris ne sont pas exécutés en parallèle, mais l'un
après l'autre.
Pour cela, les instances de WinTracer sont désormais de
simples JPanel et non plus des JFrame et ne contiennent
plus que la représentation des données. Il est donc necessaire
à la création d'un WinTracer de lui passer en paramètre
le JSlider qui gère la durée de la temporisation.
C'est la classe TestSort qui a maintenant en charge la création
d'une fenêtre principale, à laquelle on associe un WindowListener
permettant la sortie de l'application quand la fenêtre est fermée.
A noter l'utilisation des exceptions pour différencier dans la ligne
de commande les classes de tri à charger des données à
trier.
Les méthodes main(String []) de InsertionSort
et QuickSort n'étant plus compatibles avec la nouvelle
version de WinTracer, elles ont été supprimées.
Version 4.0
Cette version est similaire à la version 3.99, mais les tris sont
cette fois exécutés en parallèle. Pour cela, on utilise
les threads.