

    [Notes générales sur les schémas]


! "Diagramme de classes" : schéma qui représente les liens entre des classes de l'application. Les flèches
  représentent les liens statiques entre les classes : une flèche d'une classe A vers une classe B signifie
  que la classe A possède "connaît" et "utilise" la classe B. La classe A peut posséder un attribut de type B
  et/ou manipuler des objets de type B. La classe A peut donc appeler des méthodes de la classe B. la classe A
  "dépend de" la classe B (pour son fonctionnement elle a besoin de la classe B).
    ! Une double flèche indique une référence/utilisation mutuelle des deux classes.
    ! Note : lors du développement, il est important de ne pas faire de référence allant dans le sens inverse
      d'une flèche. Donc nulle part dans la classe B il ne doit y avoir de référence ou d'utilisation de la
      classe A.
      Respecter le "sens des flèches" des schémas de conception et des dépendances entre couches (voir plus
      bas) permet de maintenir une structure claire du code du logiciel.
        Avantages :
          - Conserver un rôle clair et bien déterminé à chaque classe.
          - Savoir dans quel ordre lire le code (il est en général plus simple de lire en premier les classes
            sans dépendances, celles dont le fonctionnement peut être étudié sans lire le code d'autres
            classes).
          - Simplifier la réutilisation des classes. Si, si, on peut parfois réutiliser des classes :) . Par
            exemple l'architecture décrite plus bas contient une référence Interface -> EtatAppli mais pas
            l'inverse. C'est ce qui permet de créer différentes interfaces graphiques pour l'application
            (par exemple une interface "classique" à base de fenêtres et une interface "web" sous forme de
            pages html sur un site).

! "Diagramme de séquence" : schéma qui représente les interactions entre des objets de différentes classes au
  cours d'un traitement donné. Le schéma représente les appels effectifs de méthodes qu'effectuent les objets,
  dans le cadre restreint d'un traitement particulier, pour décrire le rôle de chacun des objets dans ce
  traitement.

! Couleurs utilisées dans les schémas
    - Classes principales de l'appli : vert
    - Interface graphique            : bleu foncé
    - Evénements et actions          : bleu clair
    - Carte (tracer, ...)            : rouge
    - Données                        : jaune

--------------------------------------------------------------------------------------------------------------


    [Architecture générale]


Organisation générale des classes et dépendances.

    Les dépendances ressemblent globalement à ça (les groupes représentés recoupent plus ou moins les
      paquetages de plus haut niveau dans le code) :
      (la convention de sens de la flèche est la même que sur les diagrammes de classe ; sauf pour
       l'exception notée dans le commentaire, représentée avec une flèche différente)
        Stockage des données   -> Données
        Interface              -> Données
        Interface              => Evénements
        GestionnaireEvénements -> Interface, Evénements, Actions
        Actions                -> Données

Commentaire

    ! Je compte ici EtatAppli dans les données, bien que cette classe ne se trouve pas dans le paquetage
      "donnees".
    - Les classes de stockage des données (chargement/sauvegarde) utilisent les classes de données.
    - L'interface représente les données (donc accède aux classes de données).
    - L'interface génère des événements [note : les classes de l'interface ne manipulent pas réellement les
      classes d'événements ; cette ligne est néanmoins représentée ici pour faire le lien entre les événements
      et l'interface].
    - Le gestionnaire d'événements surveille les événements appliqués à l'interface graphique et exécute les
      actions correspondantes.
    - Les actions agissent sur les données (lesquelles sont observées par l'interface).
    - Les fonctions utilitaires (le paquetage "utiles") ne sont pas représentées ici et peuvent être utilisées
      par à peu près n'importe quelle autre classe.

Le fonctionnement de l'application se comprend donc mieux par couche : regarder les données (qui ne dépendent
  de rien), puis le stockage et l'interface, puis la gestion des événements et les actions. Les classes ne
  peuvent pas forcément être comprises individuellement, mais le découpage en classes (ou en modules si on
  veut faire une analogie avec des langages moins "objet", comme Pascal) permet de les comprendre par blocs,
  par fonctionnalités plutôt qu'en se demandant ce qui s'est passé avant, ce qui a mené à ce code.


--------------------------------------------------------------------------------------------------------------


    [Classes principales de l'application]


Paquetages principaux

histoiremondiale
    donnees                 Données manipulées par le logiciel (informations géographiques te historiques)
    gestion                 Outils de gestion des données de l'application, du site web, ...
    igraphique              Classes de l'interface graphique (fenêtres, ...)
        actions                 Classes encapsulant les actions déclenchées par l'utilisateur
        composants              Composants graphiques propres à l'appli (panneaux, barres de défilement, ...)
        donnees                 Données nécessaires à l'interface graphique (stockage de données pour affichage)
        evenements              Gestion des événements de l'interface
        tracercartes            Classes permettant de dessiner les cartes
    stockage                Lecture et modification des données de l'appli
    utiles                  Fonctions utilitaires diverses
    -> HistoireMondiale     Classe principale de l'application


Commentaire du diagramme de classes

    - La classe HistoireMondiale est la classe principale de l'application. Elle permet d'initialiser
      le logiciel et fournit un point d'accès à différentes informations :
        - La configuration générale de l'application (chemins d'accès aux fichiers, ...).
        - L'état actuel de l'application (options et éléments modifiables par l'utilisateur).
        - Les données manipulées par le logiciel (informations historiques, géographiques, ... qui ne changent
          pas au cours de l'exécution).
        - La fenêtre principale de l'application.
        - Le gestionnaire d'événements (classe chargée de traiter les événements déclenchés par
          l'utilisateur).

          
--------------------------------------------------------------------------------------------------------------


    [Classes principales de l'interface graphique]


Commentaire du  diagramme de classes

    - Une classe représente la fenêtre principale du logiciel. Cette fenêtre contient deux panneaux
      importants :
        - Le panneau servant à tracer la carte. Ce panneau peut demander à la classe de gestion des
          tracers de dessiner une nouvelle carte. Ce gestionnaire utilise alors les outils rassemblés
          dans TraceurCarte pour créer la nouvelle carte, puis il met à jour la carte dans le panneau.
        - Le panneau contenant le navigateur html.


--------------------------------------------------------------------------------------------------------------


    [Modèle de conception "observateur"]


Note sur les modèles de conception

    - Un modèle de conception est une solution connue et éprouvée à un problème courant de conception des
      logiciels. Les avantages et inconvénients de cette solution sont bien connus, ce qui permet aux
      développeurs d'éviter de réinventer la roue (ou d'essayer) à chaque fois qu'il rencontre un problème
      très courant.
    - Le modèle de l'observateur (observateur - observable) est un modèle de conception classique.

Principe

    - Un objet contenant des informations est déclaré comme observable et possède une liste d'observateurs.
      Il ne connaît pas leur type réel, simplement qu'il s'agit d'observateurs.
    - Les observateurs implémentent une interface les définissant comme observateurs. Cette interface
      contient une méthode à implémenter pour réagir aux modifications des objets observés.
    - Lorsque l'objet observé est modifié, il appelle la méthode de réaction aux modifications de tous les
      observateurs qui se sont inscrits.
      
Avantages

    - Les observateurs sont avertis quand les objets qu'ils observent sont modifiés et peuvent réagir
      immédiatement.
    - Les objets observés ne "connaissent" pas le nombre et le type de leurs observateurs : il n'y a pas
      de référence observable -> observé, simplement une liste d'observateurs abstraits. Ceci permet
      d'ajouter/retirer plus facilement des observateurs à un objet observable, sans avoir à le modifier. Ce
      modèle permet aussi de simplifier les dépendances telles que décrites dans l'architecture.
      
Exemple d'application dans le code du logiciel

    - L'état de l'application est observable.
    - Les composants graphiques utilisés (panneau de la carte, boutons, textes, ...) sont des observateurs de
      l'état de l'application. Ceci évite de "polluer" le code de ces données avec des références à
      l'interface et permet donc de respecter les dépendances décrites dans l'architecture de l'application.


--------------------------------------------------------------------------------------------------------------


    [Tracer d'une carte]


Le gestionnaire de tracer des cartes est un composant qui s'exécute dans un fil d'exécution séparé du reste
  de l'application. Ce fonctionnement permet de tracer les cartes en arrière-plan plutôt que de bloquer
  l'interface quand on demande une nouvelle carte, rendant l'interface plus réactive (notamment en cas
  d'événements multiples, comme le défilement de la barre des années).

Etapes

    [Fil d'exécution de Swing]
        - Lorsqu'une carte doit être redessinée, le panneau (classe PanCarte) demande au gestionnaire de
          tracer des cartes (GestTracerCartes) de réaliser une nouvelle carte pour remplacer l'ancienne.
        - Une demande est simplement ajoutée à la file d'attente et le fil d'exécution peut se poursuivre.
          Les autres événements de l'interface graphique peuvent donc être traités immédiatement.

    [Fil d'exécution du gestionnaire de cartes]
        - Le gestionnaire de tracer attend qu'une demande apparaisse dans la file d'attente.
        - Lorsqu'une demande survient, le gestionnaire de tracer utilise l'outil de dessin des
          cartes (TraceurCarte) pour créer une nouvelle image représentant la carte demandée.
        - Une fois l'image prête, le gestionnaire de tracer demande au panneau de se redessiner.

    [Fil d'exécution de Swing]
        - Lorsque le panneau reçoit la demande de se redessiner, il affiche la nouvelle carte.

        
--------------------------------------------------------------------------------------------------------------


    [Système de gestion des événements de l'application]


Afin d'éviter la dispersion du code de gestion des événements de l'interface graphique, le code de la gestion
  des événements est centralisé autant que possible.
  

Principes

    - Représentation centralisée de l'état de l'application.
    - Définition centralisée des associations événement -> action à réaliser.


Représentation centralisée de l'état de l'application

    Un objet unique rassemble les informations sur l'état de l'application. Cet objet contient ou
      référence toutes les informations sur les options et éléments modifiables du logiciel.
      Cet objet est observé par les composants graphiques sur le modèle de l'observateur : quand des
      informations sont modifiées, les observateurs sont prévenus et peuvent mettre à jour leur apparence
      en conséquence.


Principe général de gestion des événements

    - Les événements sont envoyés à un gestionnaire d'événements unique.
    - Le gestionnaire d'événements est chargé d'exécuter les actions correspondant aux événements.
    - Les actions modifient l'état de l'application.
    - Les observateurs (objets graphiques) sont prévenus de la modification et ajustent l'affichage en
      conséquence.


Définition centralisée des événements

    Lors de l'initialisation de l'application, le gestionnaire d'événements se charge de placer les écouteurs
      qui conviennent sur les composants graphiques de l'interface.
    Lorsqu'un événement survient sur un composant, le gestionnaire d'événements lance l'exécution de l'action
      associée à cet évenement.

    Note : les événements "internes" (tout ce qui ne répond pas réellement à une demande d'action/résulat de
      l'utilisateur, notamment les événements "locaux", ne modifiant pas les données partagées de
      l'application ; par exemple le redimensionnement d'un composant qui l'oblige à réorganiser sa
      présentation) sont de préférence gérés dans la classe concernée et pas dans la gestion centralisée des
      événements.


Représentation des événements

    Le gestionnaire d'événements ne manipule pas des événements AWT mais des objets propres à l'application.
      Ces objets représentent des événements d'un peu plus haut niveau que les événements AWT, par exemple
      "simple clic gauche" ou "majuscule + clic droit".
    
    L'application contient des écouteurs générant ces événements pour les envoyer au gestionnaire
      d'événements. Les écouteurs dérivent des écouteurs AWT mais filtrent les événements qu'ils reçoivent
      pour ne générer que les événements de plus haut niveau qui conviennent.
    Par exemple l'écouteur de clics gauche de l'application hérite de MouseListener et implémente
      mouseClicked(...), mais n'envoie un événement au gestionnaire d'événements que lorsqu'il s'agit
      d'un simple clic gauche.


Problème de l'accès aux composants graphiques par le gestionnaire des événements

    Le gestionnaire d'événements associe des évenements aux composants graphiques. Il reçoit également des
    événements indiquant quel type d'événement est arrivé et sur quel composant. Il doit alors déterminer quel
    est le composant concerné en comparant le composant source de l'événement avec les composants susceptibles
    de l'avoir déclenché.

    Le gestionnaire d'événements doit donc avoir un accès aux composants graphiques internes de fenêtres.
      Ce fait n'est pas très grave en soi, puisque son travail consiste à gérer les associations entre des
      événements et des composants, mais il oblige les fenêtres et autres composants à laisser un accès à
      leurs composants internes.

    Les attributs représentant ces composants internes sont donc publics (toutes les classes n'étant pas dans
      le même paquetage, l'accès ne peut pas être restreint à ce paquetage). Des accesseurs publics masquant
      des attributs privés ne serviraient qu'à masquer le nom réel du composant interne ou son emplacement
      (s'il est déplacé). Dans aucun de ces deux cas il ne semble pertinent de conserver les anciens nom et
      emplacement du composant, autant maintenir le gestionnaire d'événements à jour et l'ensemble cohérent.
      Des accesseurs publics n'apporteraient donc rien (à part du code supplémentaire).


Comparaison de cette solution avec une gestion plus décentralisée des événements (méthode standard AWT/Swing)

    Inconvénients
    
      - Rupture de l'encapsulation des classes représentant les fenêtres, panneaux : les composants
        sont accessibles directement depuis l'extérieur.
        
      - Pour définir un événement, il faut l'ajouter dans une classe centralisée. Donc quand on crée une
        nouvelle interface/vue, on est obligé de définir la gestion de ses événements dans une autre
        classe, on ne peut pas se contenter d'ajouter à cette classe des écouteurs qui envoient des
        messages au gestionnaire d'événements. Note : une version intermédiaire consiste à ne centraliser
        que la gestion des événements, pas leur déclaration (laquelle peut très bien utiliser le système
        d'écouteurs actuels [obligatoire pour utiliser le gestionnaire d'événements] et déclarer les
        écouteurs à la fin du constructeur d'un objet, pas au moment de la création de chaque bouton).
        
      - L'interface graphique doit être entièrement initialisée (les objets doivent être créés) pour pouvoir
        ensuite enregistrer les écouteurs dessus de manière centralisée.
          => Note : on peut avoir plusieurs méthodes de déclaration des événements, pour chaque fenêtre
             par exemple, et appliquer la méthode correspondante à chaque fenêtre créée.
             Si on veut pouvoir classer les événements plus librement que par fenêtre, on peut aussi
             déclarer les événements et la méthode se charge d'appliquer les événements qui conviennent
             à la fenêtre en cours de création.

    Avantages
    
      + La définition et surtout la gestion centralisée des événements fait apparaître et permet de
        modifier plus facilement les interactions entre les composants. Il n'y a même pas besoin
        d'écrire une spécification/documentation séparée des interactions entre les composants, elle apparaît
        dans le gestionnaire d'événements.


Résumé des classes de gestion des événements

    EtatAppli      : état centralisé de l'application.
    Evenement      : événement de haut niveau géré par le système.
    EcouteurXXX    : écouteurs d'événements du système.
    GestEvenements : gestionnaire centralisé des événements.
    ActionsXXX     : traitements de l'application, lancés par le gestionnaire en réponse aux événements.


Note sur les notifications envoyés aux observateurs

    Les notifications sont envoyées une par une plutôt que sous forme d'une liste. Donc si on a modifié le
      point central de la carte deux notifications sont envoyées, une pour la longitude et une pour la
      latitude. L'important est de bien faire toutes les modifications d'un coup puis d'envoyer les
      notifications, pour éviter que la carte soit redessinée plusieurs fois. Peu importe les notifications
      multiples : si deux demandes de tracer d'une nouvelle carte sont envoyées successivement, la seconde
      ne sera pas exécutée parce que le gestionnaire de tracer des cartes s'aperçoit que la carte actuelle
      correspond déjà à ces critères.


--------------------------------------------------------------------------------------------------------------


    [Exemple de gestion d'un événement]


Exemple de gestion d'un événement : le clic gauche sur le bouton loupe, qui permet d'augmenter le niveau
  de loupe.

Commentaire du  diagramme de séquence

    Etapes de l'initialisation de l'application
        - Le gestionnaire d'événements attache au bouton loupe un écouteur de clics gauche.
        - Le panneau de la carte et le texte affichant la valeur courante de la loupe s'inscrivent comme
          observateurs de l'état de l'application.

    Etapes du traitement de l'événement
        - L'utilisateur clique sur le bouton.
        - Le bouton prévient l'écouteur de clic gauche
          (les écouteurs sont des observateurs des composants graphiques).
        - L'écouteur envoie un événement "clic gauche sur le bouton loupe" au gestionnaire d'événements.
        - Le gestionnaire d'événements choisit l'opération à exécuter
          (il exécute la méthode de changement de la valeur de la loupe d'une classe d'actions).
        - La méthode modifie la valeur de la loupe dans l'état de l'application.
        - L'objet représentant l'état de l'appli indique qu'il a changé à ses observateurs :
            - Le panneau de la carte, qui lance le tracer d'une nouvelle carte.
            - Le texte affichant la valeur de la loupe, qui affiche la nouvelle valeur.
            
--------------------------------------------------------------------------------------------------------------


    [Complément sur l'accès aux données et leur stockage]


L'accès aux données est donc séparé en deux parties :
    - Les classes DonneesGeographiques, DonneesHistoriques et DonneesIGraphique, qui permettent au reste de
      l'application de demander des données (les territoires présents à une époque donnée, la liste des
      civilisations, la liste des fleuves, ...).
      Ces classes permettent de faire abstraction de la méthode de stockage des données, de charger tout ou
      partie des données au démarrage de l'application, de gérer du cache, ...
    - Les classes de stockage, qui permettent de récupérer les données depuis leur forme de stockage
      (fichiers, base de données, ...).
      Ces classes contiennent le code dépendant de la méthode de stockage des données.


--------------------------------------------------------------------------------------------------------------


    [Lancement de l'application et emplacement des données]


Le répertoire contenant les données de l'application est déterminé au lancement du programme.

Plusieurs moyens d'indiquer ce chemin (par ordre de priorité décroissante) :
    - Variable d'environnement.
    - Chemin absolu inscrit dans le fichier de configuration.
    - Sinon le chemin par défaut est utilisé.
    - Chemin relatif (au chemin spécifié dans la variable d'environnement ou au chemin par défaut), inscrit
      dans le fichier de configuration.

La variable d'environnement permet de faire varier l'emplacement par l'utilisateur s'il souhaite jongler avec
  plusieurs jeux de données. Ou encore gérer le fait que les données ne sont pas forcément installées avec
  le programme et que leur emplacement peut varier d'un système à l'autre.

Les chemins spécifiés dans le fichier de configuration permettent à l'utilisateur de choisir un autre
  emplacement pour les données (notamment s'il en a plusieurs versions).

Les détails du fonctionnement dans la classe ConfigAppli.

