Télécharger la présentation
La présentation est en train de télécharger. S'il vous plaît, attendez
Publié parDavid Marceau Modifié depuis plus de 8 années
1
Master ISIDIS1 Programmation Orientée Objet Avancée
2
Master ISIDIS2 Plan MVC Réflexivité Design Pattern
3
Master ISIDIS3 MVC
4
Master ISIDIS4 MVC (Model-View-Controller) Le modèle gère les données La vue affiche les données Le contrôleur gère la communication et les mise-à-jour
5
Master ISIDIS5 Exemple (1) class PlusouMoinsMVC { Model model; View view; Controller control; public PlusouMoinsMVC() { model = new Model(); control = new Controller(); view = new View( control); control. setModel( model); control. setView( view); view. setVisible( true); view. addWindowListener( new WindowCloser()); } public static void main (String[] argv) { PlusouMoinsMVC r = new PlusouMoinsMVC(); }
6
Master ISIDIS6 Exemple (2) Le modèle contient la donnée La mise-à-jour par update() Le renseignement par getValue() class Model { int compteur; Model() { compteur = 0; } void update( int incr) { compteur += incr; } int getValue() { return compteur; }
7
Master ISIDIS7 Exemple (3) La vue affiche les composants et les données La mise-à-jour du texte de l’étiquette est faite par update(). La notification de modifications au contrôleur. C’est le contrôleur qui écoute ! Les renseignements sont pris par getValue(). La vue se débrouille pour les obtenir. class View extends Frame { Button oui = new Button("Up !"); Button non = new Button("Down !"); Label diff = new Label("0", Label.CENTER); public View(Controller c) { super("Plus ou moins"); add(oui, BorderLayout.NORTH); add(non, BorderLayout.SOUTH); add(diff, BorderLayout.CENTER); oui.addActionListener(c); non.addActionListener(c); pack(); } void update(int compte) { diff.setText(Integer.toString(compte)); } int getValue(ActionEvent e) { Button b = (Button) e.getSource(); return (b == oui) ? 1 : -1; }
8
Master ISIDIS8 Exemple (4) Le contrôleur : Réveillé par les actions produites dans la vue Récupère des information dans la vue Met à jour dans le modèle Récupère la nouvelle valeur dans le modèle La transmet pour affichage à la vue class Controller implements ActionListener { private Model m; private View v; public void actionPerformed(ActionEvent e) { m.update(v.getValue(e)); v.update(m.getValue()); } public void setModel (Model m) { this.m = m; } public void setView (View v) { this.v = v; }
9
Master ISIDIS9 Réflexivité
10
Master ISIDIS10 Réflexion L'objet Class, qui contient toutes les informations relative à la classe (on l'appelle parfois meta-class). En fait, l'objet Class est utilisé pour créer tous les objets « habituels » d'une classe. Il y a un objet Class pour chacune des classes d'un programme. A chaque fois qu'une classe est écrite et compilée, un unique objet de type Class est aussi créé
11
Master ISIDIS11 Réflexion Durant l'exécution, lorsqu'un nouvel objet de cette classe doit être créé, la JVM qui exécute le programme vérifie d'abord si l'objet Class associé est déjà chargé. Si non, la JVM le charge en cherchant un fichier.class du même nom. Ainsi un programme Java n'est pas totalement chargé en mémoire lorsqu'il démarre, contrairement à beaucoup de langages classiques. Une fois que l'objet Class est en mémoire, il est utilisé pour créer tous les objets de ce type.
12
Master ISIDIS12 Réflexion Class.forName("NomClasse"); Cette méthode est une méthode static de Class (qui appartient à tous les objets Class ). Un objet Class est comme tous les autres objets, il est donc possible d'obtenir sa référence et de la manipuler (c'est ce que fait le chargeur de classes). Un des moyens d'obtenir une référence sur un objet Class est la méthode forName(), qui prend en paramètre une chaîne de caractères contenant le nom de la classe dont vous voulez la référence. Elle retourne une référence sur un objet Class.
13
Master ISIDIS13 Réflexion La classe Class (décrite précédemment dans ce chapitre) supporte le concept de réflexion, et une bibliothèque additionnelle, java.lang.reflect, contenant les classes Field, Method, et Constructor (chacune implémentant l'interface Member ). Les objets de ce type sont créés dynamiquement par la JVM pour représenter les membres correspondants d'une classe inconnue.
14
Master ISIDIS14 Réflexion On peut alors utiliser les constructeurs pour créer de nouveaux objets, les méthodes get() et set() pour lire et modifier les champs associés à des objets Field, et la méthode invoke() pour appeler une méthode associée à un objet Method. De plus, on peut utiliser les méthodes très pratiques getFields(), getMethods(), getConstructors(), etc. retournant un tableau représentant respectivement des champs, méthodes et constructeurs (pour en savoir plus, jetez un oeil à la documentation en ligne de la classe Class ). Ainsi, l'information sur la classe d'objets inconnus peut être totalement déterminée dynamiquement, sans rien en savoir à la compilation.
15
Master ISIDIS15 Réflexion : un exemple Un extracteur de méthodes public class ShowMethods { public static void main(String[] args) { try { Class c = Class.forName(args[0]); Method[] m = c.getMethods(); Constructor[] ctor = c.getConstructors(); if(args.length == 1) { for (int i = 0; i < m.length; i++) System.out.println(m[i]); for (int i = 0; i < ctor.length; i++) System.out.println(ctor[i]); } else { for (int i = 0; i < m.length; i++) if(m[i].toString().indexOf(args[1])!= -1) System.out.println(m[i]); for (int i = 0; i < ctor.length; i++) if(ctor[i].toString().indexOf(args[1])!= -1) System.out.println(ctor[i]); } } catch(ClassNotFoundException e) { System.err.println("Classe non trouvée : " + e); } } }
16
Master ISIDIS16 Design Pattern
17
Master ISIDIS17 Modélisation d’objets … Des objectifs parfois antagonistes : Encapsuler des données sans en empêcher l’accès Trouver un bon niveau de granularité des objets Limiter les dépendances entre les objets Concevoir des objets polyvalents, flexibles, réutilisables Simplicité d’utilisation Implémentation performante …
18
Master ISIDIS18 Modélisation d’applications Modéliser correctement une application : Processus complexe Expertise acquise au fil des expériences Problèmes de conception récurrents : Design Pattern
19
Master ISIDIS19 Un Design Pattern (1) Nom Exposé du problème Contexte de mise en œuvre, contraintes limitantes Description de la solution proposée Conseils d’implémentation Exemple d’implémentation Confrontation avec d’autres Design Pattern
20
Master ISIDIS20 Exemple Nom Salle d’attente Exposé du problème On doit attendre Description de la solution proposée Toujours relaxante et pas confinée Conséquence Attente active ou passive ? Durée de l ’attente ? Distraction ? Exemple d’implémentation Aéroport, dentiste, …
21
Master ISIDIS21 Un Design Pattern (2) Modèles parfois (souvent ?) triviaux Relative standardisation du nommage des Design Patterns
22
Master ISIDIS22 Un Design Pattern (3) Ce que c’est : Description d ’une solution classique à un problème récurent Décrit une partie de la solution… Avec des relations avec le système et les autres parties... C ’est une technique d ’architecture logicielle
23
Master ISIDIS23 Un Design Pattern (4) Ce que ce n’est pas : Une brique : Un pattern dépend de son environnement Une règle : Un pattern ne peut pas s ’appliquer mécaniquement Une méthode : Ne guide pas une prise de décision ; un pattern est la décision prise Nouveau : Lao-Tzu (-550) travaillait déjà sur les patterns...
24
Master ISIDIS24 Un Design Pattern (5) Avantages : Un vocabulaire commun Capitalisation de l ’expérience Un niveau d ’abstraction plus élevé qui permet d ’élaborer des constructions logicielles de meilleure qualité Réduire la complexité Guide/catalogue de solutions
25
Master ISIDIS25 Un Design Pattern (6) Inconvénients : Effort de synthèse ; reconnaître, abstraire, … Apprentissage, expérience Les patterns « se dissolvent » en étant utilisés Nombreux… Lesquels sont identiques ? De niveaux différents … des patterns s ’appuient sur d ’autres…
26
Master ISIDIS26 Principales classes de Design Patterns Patterns de création : Création d’objet, sans instanciation directe d’une classe Patterns de composition : Composition de groupe d’objets Patterns comportementaux : Modélisation de communications inter- objets et du flot de données
27
Master ISIDIS27 Les Design Patterns Purpose CreationalStructuralBehavioral Scope Class Factory Method AdapterInterpreter Template Method Object Abstract Factory Builder Prototype Singleton Adapter Bridge Composite Decorator Facade Proxy Chain of Responsibility Command Iterator Mediator Memento Flyweight Observer State Strategy Visitor
28
Master ISIDIS28 Application des formes lors de la conception Trouver les bons objets Bien choisir la granularité des objets Spécifier les interfaces des objets Spécifier l'implantation des objets Mieux réutiliser Héritage vs composition Délégation Compiled-Time vs Run-Time Structures Concevoir pour l'évolution
29
Master ISIDIS29 Creational patterns Formes de création : Abstraire le processus d'instanciation Rendre indépendant de la façon dont les objets sont créés, composés, assemblés, représentés Encapsuler la connaissance de la classe concrète qui instancie Cacher ce qui est créé, qui crée, comment et quand
30
Master ISIDIS30 Abstract Factory (1) Objectif : obtenir des instances de classes implémentant des interfaces connues, mais en ignorant le type réel de la classe obtenue Exemple : une application gérant des documents polymorphes générateurs de composants graphiques supportant une multitude de look-and-feels
31
Master ISIDIS31 Abstract Factory (2) Abstract Factory : Déclare une interface pour les opérations qui créent les objets abstraits ConcreteFactory : Implémente les opérations qui créent les objets concrets AbstractProduct : Déclare une interface pour un type d’objet ConcreteProduct : Définit un objet qui va être créé par la « ConcreteFactory » correspondante Implémente l’interface de « AbstractProduct » Client : Utilise juste les interfaces déclarées par les classes « AbstractFactory » et « ProductFactory »
32
Master ISIDIS32 Abstract Factory (3) Le pattern « AbstractFactory » permet de : Isoler les classes concrètes Échanger facilement les familles de produit Rendre consistant les classes Mais : L’évolution n’est pas facile
33
Master ISIDIS33 Factory Method / Virtual Constructor (1) On utilise le FactoryMethod lorsque : une classe ne peut anticiper la classe de l'objet qu'elle doit construire une classe délègue la responsabilité de la création à ses sous-classes, tout en concentrant l'interface dans une classe unique
34
Master ISIDIS34 Factory Method / Virtual Constructor (2) Permet de : Relier les sous-classes Connecter des hiérarchies de classes parallèles
35
Master ISIDIS35 Factory Method / Virtual Constructor (3) Exemple :
36
Master ISIDIS36 Prototype (1) Objectif : obtenir une instance d’un objet à partir d’une autre instance Exemple : drag-and-drop de composants inconnus avec touche Ctrl enfoncée
37
Master ISIDIS37 Prototype (2) Permet de : Ajouter et enlever des produits durant l’exécution Spécifier de nouveaux objets en faisant varier les valeurs Réduire les sous-classes Configurer une application dynamiquement avec des classes
38
Master ISIDIS38 Prototype (3) Exemple :
39
Master ISIDIS39 Singleton (1) Objectif : s’assurer qu’une seule instance d’un type spécifique existe dans le système et fournit l’accès à cet objet Exemple : un spooler d’impression
40
Master ISIDIS40 Singleton (2) Permet de : Contrôler l’accès à une seule instance Contrôler le nombre d’instances Rendre les opérations plus flexibles que si on utilisait les classes
41
Master ISIDIS41 Structural Wrapper Formes de structure : Comment les objets sont assemblés Les patterns sont complémentaires les uns des autres
42
Master ISIDIS42 Adapter / Wrapper (1) Objectif : obtenir un objet qui permet d’en utiliser un autre en conformité avec une certaine interface Exemple : mise en « conformité » de composants d’origines diverses
43
Master ISIDIS43 Adapter / Wrapper (2) On utilise l'Adapter lorsque on veut utiliser : Une classe existante dont l'interface ne convient pas Plusieurs sous-classes mais il est est coûteux de redéfinir l'interface de chaque sous-classe en les sous-classant. Un adapter peut adapter l'interface au niveau du parent.
44
Master ISIDIS44 Adapter / Wrapper (3) Exemple :
45
Master ISIDIS45 Proxy / Surrogate (1) Objectif : obtenir un objet qui agit comme intermédiaire dans la communication avec un autre objet (un « passeur d’ordre ») Exemples : un objet qui reporte les opérations coûteuses au moment où on utilise réellement les résultats de ces opérations (chargement d’une image à la fin d’un document, …) ; un objet qui transforme une collection en lecture-seule
46
Master ISIDIS46 Proxy / Surrogate (2) Exemple :
47
Master ISIDIS47 Proxy / Surrogate (3) Exemple :
48
Master ISIDIS48 Composite (1) Objectif : manipuler indifféremment des objets atomiques ou des agrégats d’objets Exemple : une application manipulant des formes graphiques et des compositions de ces formes
49
Master ISIDIS49 Composite (2) Conséquences : Définit des hiérarchies de classes combinant des objets primitifs et des objets composites Rend le client plus simple Facilite l’ajout de nouveaux composants
50
Master ISIDIS50 Composite (3) Exemple :
51
Master ISIDIS51 Decorator (1) Objectif : ajouter à des instances spécifiques des comportements spécifiques Exemple : bordure d’un composant graphique
52
Master ISIDIS52 Decorator (2) Exemple :
53
Master ISIDIS53 Facade (1) Objectif : fournir une interface simplifiée et limitée à un système complexe Exemple : donner accès à des passes spécifiques d’un compilateur
54
Master ISIDIS54 Facade (2) Exemple :
55
Master ISIDIS55 Behavioural Patterns Formes de comportement pour décrire : Des algorithmes Des comportements entre objets Des formes de communication entre objet
56
Master ISIDIS56 Chain of responsibility (1) On utilise Chain of Responsibility lorsque : plus d'un objet peut traiter une requête, et il n'est pas connu a priori l'ensemble des objets pouvant traiter une requête est construit dynamiquement
57
Master ISIDIS57 Chain of responsibility (2) Caractéristiques Réduit le couplage Ajoute de la flexibilité en assignant de la responsabilité aux objets
58
Master ISIDIS58 Chain of responsibility (3) Exemple :
59
Master ISIDIS59 Chain of responsibility (4) Exemple :
60
Master ISIDIS60 Command Objectif : réifier une commande en un objet embarquant d’éventuels paramètres Exemple : uniformiser les différentes méthodes de commande d’un système et gérer l’undo et le redo
61
Master ISIDIS61 Interpreter (1) On utilise Interpreter lorsqu'il faut interpréter un langage et que : La grammaire est simple L'efficacité n'est pas un paramètre critique
62
Master ISIDIS62 Interpreter (2) Soit la grammaire suivante :
63
Master ISIDIS63 Interpreter (3) Représentation de l’expression régulière ‘raining & (dogs | cats)*’
64
Master ISIDIS64 Interpreter (4) Modèle de l’interpréteur :
65
Master ISIDIS65 Iterator Objectif : permettre d’itérer de manière générique sur les éléments d’une collection, quelle que soit la nature des éléments ou de la collection Exemple : une collection ordonnée
66
Master ISIDIS66 Mediator (1) On utilise Mediator lorsque : Quand de nombreux objets doivent communiquer ensemble La réutilisation d'un objet est délicate car il référence et communique avec de nombreux autres objets
67
Master ISIDIS67 Mediator (2) Exemple :
68
Master ISIDIS68 Memento On utilise Memento lorsque : On veut sauvegarder tout ou partie de l'état d'un objet pour éventuellement pouvoir le restaurer, et Une interface directe pour obtenir l'état de l'objet briserait l'encapsulation
69
Master ISIDIS69 Observer / Listener Objectif : permettre à un objet d’informer d’autres objets qu’il ne connaît pas de l’évolution de son état interne Exemple : un bouton à la suite d’un click
70
Master ISIDIS70 Observeurs Une classe : Observable Un objet observable issu de cette classe dispose, entre autres, des méthodes suivantes : addObserver(Observer o) permet de rajouter un observeur setChanged() indique un changement de l’état de l’objet notifyObservers(Object arg) si l'état de l'objet a été changé, l'appel à cette méthode permet de notifier les observateurs potentiels de ce changement, par l'appel de la méthode update avec pour arguments l'objet observé et l'argument arg
71
Master ISIDIS71 Observeurs (suite) Une interface : Observer Permet de décrire un objet qui souhaite être informé du changement d’état d’objets issus de la classe Observable Introduit une seule méthode : public void update(Observable o, Object arg) qui est appelée lorsque o change d’état
72
Master ISIDIS72 Observeurs : Exemple (1) L’observé class TableObservable extends Observable { Hashtable table = new Hashtable(); public synchronized Object put(Object clé, Object valeur) { setChanged(); notifyObservers(clé); return table.put(clé, valeur); } public synchronized Object get(Object clé) { return table.get(clé); } public synchronized boolean containsKey(Object clé) { return table.containsKey(clé); } public synchronized Enumeration keys() { return table.keys(); } public int size() { return table.size(); }
73
Master ISIDIS73 Observeurs : Exemple (2) L’observeur class ObservateurDeTable implements Observer { public ObservateurDeTable(Observable o) { o.addObserver(this); } public void update(Observable o, Object arg) { System.out.println("J'observe l'objet : "+o+"\nqui m'envoie :"+arg); }
74
Master ISIDIS74 Observeurs : Exemple (3) class TestObserver{ public static void main (String[] args) { StringTokenizer lstMots = new StringTokenizer(args[0], ",."); TableObservable table = new TableObservable(); ObservateurDeTable observateur = new ObservateurDeTable(table); while (lstMots.hasMoreTokens()) { String mot = lstMots.nextToken(); if (!table.containsKey(mot)) table.put(mot, new Integer(1)); else { Integer nbre = (Integer) table.get(mot); Integer i = new Integer(1+nbre.intValue()); table.put(mot, i); } System.out.print("==> Dans la phrase \""); System.out.print(args[0]); System.out.print("\",\n il y a "); System.out.print(table.size()); System.out.println(" mots différents qui sont:"); for (Enumeration e = table.keys(); e.hasMoreElements(); ) { String mot = (String) e.nextElement(); System.out.println("==> "+mot+" ("+table.get(mot)+" fois)"); }
75
Master ISIDIS75 Observeurs : Exemple (4) Trace de l’exécution $ java TestObserver "Voila ma phrase, ma courte phrase." J'observe l'objet : TableObservable@80ca7b7 qui m'envoie : Voila J'observe l'objet : TableObservable@80ca7b7 qui m'envoie : ma J'observe l'objet : TableObservable@80ca7b7 qui m'envoie : phrase J'observe l'objet : TableObservable@80ca7b7 qui m'envoie : ma J'observe l'objet : TableObservable@80ca7b7 qui m'envoie : courte J'observe l'objet : TableObservable@80ca7b7 qui m'envoie : phrase ==> Dans la phrase "Voila ma phrase, ma courte phrase.", il y a 4 mots différents qui sont: ==> phrase (2 fois) ==> ma (2 fois) ==> courte (1 fois) ==> Voila (1 fois) $
76
Master ISIDIS76 Strategy / Policy (1) Objectif : utiliser de manière non spécifique une collection d’algorithme proches Exemple : algorithmes de tris de collections de données
77
Master ISIDIS77 Strategy / Policy (2) Exemple :
78
Master ISIDIS78 State (1) Objectif : un objet qui change de comportement en fonction de son état interne Exemple : une socket TCP (état non connectée, connectée, en attente de connection)
79
Master ISIDIS79 State (2) Exemple :
80
Master ISIDIS80 Template Method (1) On utilise TemplateMethod : Pour implanter une partie invariante d'un algorithme. Pour partager des comportements communs d'une hiérarchie de classes. Pour contrôler des extensions de sous-classe.
81
Master ISIDIS81 Template Method (2) Exemple :
82
Master ISIDIS82 Visitor (1) Objectif : découpler une structure des opérations sur cette structure Exemple : analyses/transformations d’arbres de syntaxe abstraite dans un compilateur
83
Master ISIDIS83 Visitor (2) Séquences :
84
Master ISIDIS84 Trouver les bons objets Les patterns proposent des abstractions qui n'apparaissent pas « naturellement » en observant le monde réel : Composite : permet de traiter uniformément une structure d'objets hétérogènes Strategy : permet d'implanter une famille d'algorithmes interchangeables State Ils améliorent la flexibilité et la réutilisabilité
85
Master ISIDIS85 Bien choisir la granularité des objets La taille des objets peut varier considérablement ; comment choisir ce qui doit être décomposé ou au contraire regroupé ? Facade Flyweight Abstract Factory Builder
86
Master ISIDIS86 Spécifier les interfaces des objets Qu’est-ce qui fait partie d’un objet ou non ? Memento : mémorise les états, retour arrière Decorator : augmente l'interface Proxy : interface délégué Visitor : regroupe des interfaces Facade : cache une structure complexe d'objet
87
Master ISIDIS87 Spécifier l'implantation des objets Différence type-classe… Chain of Responsibility ; même interface, mais implantations différentes Composite ; les Components ont une même interface dont l'implantation est en partie partagée dans le Composite Command, Observer, State, Strategy ne sont souvent que des interfaces abstraites Prototype, Singleton, Factory, Builder sont des abstractions pour créer des objets qui permettent de penser en termes d'interfaces et de leur associer différentes implantations
88
Master ISIDIS88 Concevoir pour l’évolution (1) Quelques raisons de « reengineering » : Création d'un objet en référençant sa classe explicitement... Lien à une implantation particulière... pour éviter utilisez AbstractFactory, FactoryMethod, Prototype Dépendance d'une opération spécifique...pour rendre plus souple utilisez Chain Of Responsibility, Command Dépendance d'une couche matérielle ou logicielle...AbstractFactory, Bridge
89
Master ISIDIS89 Concevoir pour l’évolution (2) Quelques raisons de « reengineering » : Dépendance d'une implantation... pour rendre plus souple utilisez AbstractFactory, Bridge, Memento, Proxy Dépendance d'un algorithme particulier... Builder, Iterator, Strategy, TemplateMethod, Strategy Couplage fort... relâcher les relations utilisez AbstractFactory, Bridge, Chain Of Responsibility, Command, Facade, Mediator, Observer
90
Master ISIDIS90 Concevoir pour l’évolution (3) Quelques raisons de « reengineering » : Etendre les fonctionnalités en sous-classant peut être couteux (tests, compréhension des superclasses, etc) utilisez aussi la délégation, la composition...Bridge, Chain Of Responsibility, Composite, Decorator, Observer, Strategy, Proxy Impossibilité de modifier une classe...absence du source, trop de répercussions, voyez Adapter, Decorator, Visitor
91
Master ISIDIS91 Design Pattern : Exemple
92
Master ISIDIS92 Une jeu de dés Un joueur lance 10 fois 2 dés Si le résultat = 7, alors score=score + 10 points A la fin, le score du joueur est enregistré dans la table des « highscores » Une premier exemple :
93
Master ISIDIS93 Diagramme d’activité menu view Highscore Start turn=0 Roll Dice turn++ Update highscore Turn<10 [highscore][start][exit] [true] [false]
94
Master ISIDIS94 Diagramme de classes
95
Master ISIDIS95 Modélisation Gestion de l’IHM Gestion de la persistance du « highscore » dans une base de données ou un fichier Réalisation d’une architecture par niveaux : application du « Layer Architectural Pattern »
96
Master ISIDIS96 Niveaux Aide à structurer une application qui peut être décomposée en groupes de sous-tâches Chaque groupe est un niveau particulier d’abstraction
97
Master ISIDIS97 Niveaux : exemple
98
Master ISIDIS98 Niveaux : structure
99
Master ISIDIS99 Niveaux : structure
100
Master ISIDIS100 Niveaux et composants
101
Master ISIDIS101 Niveaux : variantes « Relaxed Layered System » Un niveau « j » peut utiliser les services de niveaux j – 1, j – 2, … Un niveau peut être partielle opaque Des services au niveau j + 1 et les autres dans d’autres niveaux Faire des niveaux par héritage Les niveaux du plus bas niveau sont implémentés comme les classes de base Les niveaux supérieurs peuvent redéfinir (surcharger) les niveaux inférieurs
102
Master ISIDIS102 Niveaux : exemples d’utilisation Machines virtuelles : JVM et le format de code binaire API : niveaux qui encapsulent des niveaux inférieurs Windows NT : Services System Resource management (Object manager, security monitor, process manager, I/O manager, VM manager) Kernel (exception handling, interrupt, threads) HAL (Hardware Abstraction Level) Hardware
103
Master ISIDIS103 Niveaux : avantages Réutilisabilité des niveaux Support pour la standardisation (OSI) Les dépendances sont conservées localement Souplesse : Remplacement d’une ancienne implémentation avec le « Adapter Pattern » Échange dynamique avec le « Bridge Pattern »
104
Master ISIDIS104 Niveaux : inconvénients Enchaînement de comportements différents Faible efficacité Travaille inutile : les fonctions d’un niveau peuvent être appelées plusieurs fois pour un même service Difficulté pour établir une granularité correcte pour les niveaux : Pas assez de niveaux Pas de bénéfices Trop de niveaux Complexité
105
Master ISIDIS105 Application de l’architecture par niveaux
106
Master ISIDIS106 Décomposition en package UI > Core > Persist > Util >
107
Master ISIDIS107 Niveau « core » Contient les classes logiques Adapte le diagramme de classe pour l’implémentation Utilise le « Singleton »
108
Master ISIDIS108 Singleton Assure qu’un classe n’a qu’une seule instance et une seule Fournit un point d’accès à celle-ci
109
Master ISIDIS109 Niveau « core » : premier diagramme Design Analyse
110
Master ISIDIS110 Décomposition des packages UI > Core > Persist > Util >
111
Master ISIDIS111 Observeurs Dépendances du type « un à plusieurs » entres les objets Le changement d’un objet va automatiquement prévenir les observeurs
112
Master ISIDIS112 Observeurs : application Le changement d’un objet entraîne le changement d’un ensemble d’objets inconnus L’objet peut notifier un changement à un autre sans qu’il le sache à l’avance
113
Master ISIDIS113 Observeurs : conséquences Couplage abstrait entre les observés et les observeurs Permet de réaliser des communications de type « broadcast » Difficile à maintenir
114
Master ISIDIS114 Utilisation des observeurs
115
Master ISIDIS115 Observeurs : vue
116
Master ISIDIS116 Les vues sont des objets graphiques
117
Master ISIDIS117 Diagramme de séquences
118
Master ISIDIS118 Propagation des changements : Die : Randomizer : DieView 1: getValue( ) 2: setValue(int) 3: notifyObservers( ) 4: update(Observable, Object) 5: getState() 3 : JLabel 6: setText(3)
119
Master ISIDIS119 Architecture en niveaux UI Core Découplage des classes Et des interfaces
120
Master ISIDIS120 Décomposition des packages UI > Core > Persist > Util >
121
Master ISIDIS121 Pattern Factory Method Caractéristiques : Défini une interface pour créer un objet, mais laisse les sous-classes décider quelle classe instancier Laisse une classe déléguer l’instanciation aux sous-classes
122
Master ISIDIS122 Pattern Factory Method Applicabilité : Une classe ne peut anticiper la classe d’objet qu’elle doit créer Une classe veut que ses sous-classes spécifient les objets qu’elle crée
123
Master ISIDIS123 Niveau « persist » Classes techniques de persistance Assurer l’indépendance core/persist Pouvoir changer de « persistent engine » Par exemple : Persistance par « sérialisation » Persistance via une base de données relationnelle (JDBC/MySql par exemple)
124
Master ISIDIS124 Application du pattern Produit abstrait Fabrique abstraite Produit concret Fabrique concrète
125
Master ISIDIS125 Application du pattern
126
Master ISIDIS126 Résumé 1.Acrhitectural Pattern : Niveaux 2.Design Pattern : Observer, Factory, Singleton 3.Problème : Combiner les patterns pour combiner leurs forces…
127
Master ISIDIS127 Un deuxième exemple :une banque Un système bancaire basique : 1 banque, n comptes Chaque compte appartient à un client Chaque compte est crédité d’un certain montant Fonction de la banque : Débiter un compte Créditer un compte Transférer de l’argent d’un compte à un autre
128
Master ISIDIS128 Solution naïve
129
Master ISIDIS129 Solution naïve
130
Master ISIDIS130 Application du « Pattern Command » Encapsule une requête comme un objet Permet de paramétrer des clients avec différentes requêtes Supporte le undo
131
Master ISIDIS131 « Pattern Command » : conséquences Découple l’objet qui invoque la commande de celui qui sait comment la réaliser Il est facile d’ajouter de nouvelles commandes
132
Master ISIDIS132 Application du « Pattern Command »
133
Master ISIDIS133 Application du « Pattern Command »
134
Master ISIDIS134 « Composite Pattern » Compose les objets en une structure d’arbre pour représenter une hiérarchie complète ou partielle Le composite laisse le client traiter les objets individuels et les objets composites de manière uniforme
135
Master ISIDIS135 Application du « Composite Pattern »
136
Master ISIDIS136
137
Master ISIDIS137 Utilisation de Singleton
138
Master ISIDIS138 Et ainsi de suite… Sauvegarde de l’état : Memento Pattern Observer le compte : Observer Pattern Balayer tous les objets du graphe : Visitor Pattern Accès distant : Proxy Pattern …
139
Master ISIDIS139 Utilisation du proxy
140
Master ISIDIS140 Cas d’étude
141
Master ISIDIS141 Problèmes de modélisation Structure du document The choice of internal representation for the document affects nearly every aspect of Lexi's design. All editing, formatting, displaying, and textual analysis will require traversing the representation. The way we organize this information will impact the design of the rest of the application.
142
Master ISIDIS142 Problèmes de modélisation Formatage How does Lexi actually arrange text and graphics into lines and columns? What objects are responsible for carrying out different formatting policies? How do these policies interact with the document's internal representation?
143
Master ISIDIS143 Problèmes de modélisation Amélioration de l’IHM Lexi's user interface includes scroll bars, borders, and drop shadows that embellish the WYSIWYG document interface. Such embellishments are likely to change as Lexi's user interface evolves. Hence it's important to be able to add and remove embellishments easily without affecting the rest of the application.
144
Master ISIDIS144 Problèmes de modélisation Supporter le « look-and-feel » Lexi should adapt easily to different look- and-feel standards such as Motif and Windows and Mac without major modification.
145
Master ISIDIS145 Problèmes de modélisation Correction orthographique et césure How does Lexi support analytical operations such as checking for misspelled words and determining hyphenation points? How can we minimize the number of classes we have to modify to add a new analytical operation?
146
Master ISIDIS146
147
Master ISIDIS147
148
Master ISIDIS148
149
Master ISIDIS149
150
Master ISIDIS150
151
Master ISIDIS151
152
Master ISIDIS152
153
Master ISIDIS153
154
Master ISIDIS154
155
Master ISIDIS155
156
Master ISIDIS156
157
Master ISIDIS157
158
Master ISIDIS158
159
Master ISIDIS159
160
Master ISIDIS160
161
Master ISIDIS161
162
Master ISIDIS162 Autres exemple d’utilisation des patterns : SWING
163
Master ISIDIS163 Swing Swing est une extension des AWT nombreux nouveaux composants nombreuses facilités séparation entre : modèle (données) aspect visuel (UI) contrôle Les composants sont légers, sauf JApplet, JWindow, JFrame, JDialog Ces derniers ont une structure spéciale
164
Master ISIDIS164 Swing – l’arbre d’héritage
165
Master ISIDIS165 JFrame Une JFrame contient une fille unique, de la classe JRootPane. Cette fille contient deux filles, glassPane ( JPanel ) et layeredPane ( JLayeredPane ). La layeredPane a deux filles, contentPane (un Container ) et menuBar (null JMenuBar ). On travaille dans contentPane. JApplet, JWindow et JDialog sont semblables. class Tout extends JFrame { Tout() { JPanel panel; getContentPane().add(panel);... }... }
166
Master ISIDIS166 Modèles et vues Les composants (sauf les conteneurs) ont un modèle qui contient les données associées ButtonModel pour les boutons ListModel pour les données d’un JList TableModel pour les JTable TreeModel pour les JTree Document pour tous les composants de texte La vue d’un composant sont agrégés en un délégué UI (User Interface) détermine le look-and-feel du composant (bord, couleur, ombre, forme des coches) peut- être changé est parfois spécifié par un dessinateur (renderer) à un niveau plus élevé un changement global, pour tous les composants, est le pluggable look and feel (plaf) trois implémentations (quatre ave le Mac) existent : Windows, CDE/ Motif, Metal (défaut). Le contrôle est assuré par le modèle.
167
Master ISIDIS167 Look and Feel Trois “look and feel” existent, de noms "com. sun. java. swing. plaf. windows.WindowsLookAndFeel" "com. sun. java. swing. plaf. motif.MotifLookAndFeel" "javax. swing. plaf. metal. MetalLookAndFeel” On essaye de l’utiliser par UIManager.setLookAndFeel(lf); et de l’appliquer à la racine de l’arbre par SwingUtilities. updateComponentTreeUI (SwingUtilities.getRoot(this));
168
Master ISIDIS168 Actions Moyen commode pour définir une entrée (dans un menu) et simultanément y attacher un auditeur Entrée simultanée dans Toolbar possible Tout changement dans l’un se reflète sur l’autre (grisé etc.)
169
Master ISIDIS169 AbstractAction AbstractAction est une classe abstraite elle implémente l’interface Action Action étend ActionListener la seule méthode à écrire est actionPerformed() Les conteneurs JMenu, JPopupMenu et JToolBar honorent les actions : un même objet d’une classe implémentant AbstractAction peut être « ajouté » à plusieurs de ces conteneurs. les diverses instances opèrent de concert. par exemple, un objet ajouté à un menu et à une barre d’outils est activé ou désactivé simultanément dans les deux. Les classes dérivées de AbstractAction sont utiles quand une même action peut être déclenchée de plusieurs manières.
170
Master ISIDIS170 Emploi d’AbstractAction Création d’une classe qui étend AbstractAction Utilisation comme ActionListener Utilisation dans un menu et dans une barre d’outils class MonAction extends AbstractAction { public void actionPerformed(ActionEvent e) {... } Action monAction = new MonAction(); JButton b = new JButton("Hello"); b. addActionListener(monAction); Action copyAction = new MonAction("Copy"); JMenu menu = new JMenu("Edit"); JToolBar tools = new JToolBar(); JMenuItem copyItem = menu.add(copyAction); JButton copyBtn = tools.add(copyAction);
171
Master ISIDIS171 JTabbedPane Groupe une liste de conteneurs repérés par des onglets. Création: Ajout de conteneurs à un tabbedPane : JTabbedPane() JTabbedPane( int cotéOnglets) addTab(String texteOnglet, Component composant) addTab(String texteOnglet, Icon icone, Component composant) addTab(String texteOnglet, Icon icone, Component composant, String toolTipText)
172
Master ISIDIS172 JTabbedPane Feuille initiale Récupérer le choix Et la feuille elle-même Nombre total de feuilles tabbedPane.setSelectedIndex(int numero) int tabbedPane. getSelectedIndex() Component tabbedPane.getComponentAt(int numero); int tabbedPane.getTabCount();
173
Master ISIDIS173 JTabbedPane : exemple (1) Pour naviguer ajouter, enlever les feuilles choisir la position des onglets De plus un message affiche le numéro de l’onglet, à chaque changement
174
Master ISIDIS174 JTabbedPane : exemple (2) Acteurs principaux class Panneau extends JPanel implements ActionListener { String [] imageNames = {"arques","berstel","crochemore","desarmenien",...}; ImageIcon[] images = new ImageIcon[imageNames.length]; //les images montrées ImageIcon tabimage; //l’icône dans les onglets JTabbedPane tabbedPane; //le panneau à feuilles String[] boutonNames = {"TOP","BOTTOM","LEFT","RIGHT","add","remove"}; JButton[] boutons = new JButton[boutonNames.length]; //les boutons de gestion JLabel statut; //le message d’état AudioClip layoutson, tabson; //les sons des actions Panneau() {} //création de la scène void createTab() {} //ajoute une feuille et son onglet void killTab() {} //supprime une feuille void setStatus(int index) {...} //gestion du message public void actionPerformed(ActionEvent e) {...} //les actions des boutons }
175
Master ISIDIS175 JTabbedPane : exemple (3) Création/Suppression de feuilles public void createTab() { JLabel feuille = null; int ong = tabbedPane.getTabCount(); feuille = new JLabel(imageNames[ong % images.length], images[ong % images.length], SwingConstants.CENTER); feuille.setOpaque(true); feuille.setBackground(Color. green); tabbedPane.addTab("Feuille No " + ong, tabimage, feuille); tabbedPane.setSelectedIndex(ong); setStatus(ong); } public void killTab() { // dernière if (tabbedPane.getTabCount()> 0) { tabbedPane.removeTabAt(tabbedPane.getTabCount() - 1); setStatus(tabbedPane.getSelectedIndex()); } public void setStatus( int index) { if (index > -1) statut.setText("Feuille choisie: " + index); else statut.setText("Pas de feuille choisie"); }
176
Master ISIDIS176 JTabbedPane : exemple (4) Les actions des boutons La classe SwingConstants contient les constantes de placement public void actionPerformed(ActionEvent e) { String lib = ((JButton) e.getSource()).getActionCommand(); if (lib.equals(boutonNames[0])) { tabbedPane.setTabPlacement(SwingConstants.TOP); layoutson.play(); } else if (lib.equals(boutonNames[1])) { tabbedPane.setTabPlacement(SwingConstants.BOTTOM); layoutson.play(); } else if (lib.equals(boutonNames[2])) { tabbedPane.setTabPlacement(SwingConstants.LEFT); layoutson.play(); } else if (lib.equals(boutonNames[3])) { tabbedPane.setTabPlacement(SwingConstants.RIGHT); layoutson.play(); } else if (lib.equals(boutonNames[4])) createTab(); else if(lib.equals(boutonNames[5])) killTab(); }
177
Master ISIDIS177 JTabbedPane : exemple (5) Panneau() { tabimage = new ImageIcon("gifs/tabimage.gif"); for (int i = 0 ; i < images.length; i++) images[i] = new ImageIcon("gifs/" + imageNames[i] +".jpg"); for (int i = 0; i < boutons.length; i++) boutons[i] = new JButton(boutonNames[i]); statut = new JLabel(); JPanel buttonPanel = new JPanel(); buttonPanel.setLayout(new GridLayout(0,1)); for (int i = 0; i < boutons.length ; i++){ boutons[i].addActionListener(this); buttonPanel.add(boutons[i]); } JPanel leftPanel = new JPanel(); leftPanel.add(buttonPanel); tabbedPane = new JTabbedPane(SwingConstants.TOP); createTab(); createTab(); tabbedPane.addChangeListener(new ChangeListener(){ public void stateChanged(ChangeEvent e) { setStatus(((JTabbedPane) e.getSource()).getSelectedIndex()); tabson.play(); } }); setLayout(new BorderLayout()); add(leftPanel, BorderLayout.WEST); add(statut, BorderLayout.SOUTH); add(tabbedPane, BorderLayout.CENTER); }
178
Master ISIDIS178 JTabbedPane : exemple (6) On modifie le contenu après le chargement La méthode revalidate sert à « forcer » le réaffichage. public void init() { JLabel loading = new JLabel("Initialisation en cours...", JLabel.CENTER); setContentPane(loading); setVisible(true); getRootPane().revalidate(); try { Thread. sleep( 1000); } catch (InterruptedException e) {}; layoutson = getAudioClip(getCodeBase(), "switch.wav"); tabson = getAudioClip(getCodeBase(), "tab.wav"); Panneau panneau = new Panneau(); panneau.addAudioClips(layoutson, tabson); setContentPane(panneau); }
179
Master ISIDIS179 JScrollPane Gère automatiquement des ascenseurs autour de son composant central qui est un JViewPort. Constructeurs principaux Une « vue » s’ajoute au JViewPort, si elle ne l’est dans le constructeur, par JScrollPane() JScrollPane(Component view) scrollPane.getViewPort().add(view)
180
Master ISIDIS180 Exemple class ScrollPanel extends JPanel { public ScrollPanel() { setLayout(new BorderLayout()); Icon iconeTigre = new ImageIcon("BigTiger.gif"); JLabel etiquetteTigre = new JLabel(iconeTigre); JScrollPane scrollPane = new JScrollPane(etiquetteTigre); add(scrollPane, BorderLayout.CENTER); }
181
Master ISIDIS181 JSplitPane Panneau à compartiments, chaque compartiment est ajustable Seule une classe de look-and-feel est nécessaire. Panneau à séparation verticale ou horizontale Constructeurs : JSplitPane(int orientation, boolean dessinContinu, Component gauche, Component droit) JSplitPane(int orientation, Component gauche, Component droit) JSplitPane(int orientation, boolean dessinContinu) JSplitPane(int orientation) JSplitPane() // horizontal par défaut
182
Master ISIDIS182 JSplitPane : exemple (1) La taille de la barre de séparation peut être réglée par setDividerSize(int taille) L’affichage continue spécifié explicitement par setContinuousLayout(boolean dessinContinu) Poignée d’ouverture/ fermeture spécifiées par setOneTouchExpandable(boolean ouvrable) ImageIcon bleue = new ImageIcon("bleue.gif"); aireGauche = new PanneauBoules(150, bleue.getImage()); ImageIcon rouge = new ImageIcon("rouge.gif"); aireDroite = new PanneauBoules(150, rouge.getImage()); JSplitPane sp = new JSplitPane(JSplitPane.HORIZONTAL_ SPLIT, aireGauche, aireDroite); sp.setDividerSize(5); sp.setContinuousLayout(true); getContentPane().add(sp, BorderLayout.CENTER);
183
Master ISIDIS183 JSplitPane : exemple (2) public class Split extends JFrame { protected PanneauBoules aireGauche, aireDroite; public Split() {... ImageIcon bleue = new ImageIcon("bleue. gif"); aireGauche = new PanneauBoules(150, bleue.getImage()); ImageIcon rouge = new ImageIcon("rouge.gif"); aireDroite = new PanneauBoules(150, rouge.getImage()); JSplitPane sp = new JSplitPane(JSplitPane. HORIZONTAL_ SPLIT, aireGauche, aireDroite); sp.setDividerSize(5); sp.setContinuousLayout(true); getContentPane().add(sp, BorderLayout.CENTER); setVisible(true);... new Thread(aireGauche).start(); new Thread(aireDroite).start(); }
184
Master ISIDIS184 JSplitPane : exemple (3) class PanneauBoules extends JPanel implements Runnable, ComponentListener { Boule[] boules; Image img; Dimension dim; int sommeil; public PanneauBoules(int nBoules, Image img) { sommeil = 10; this.img = img; setBackground(Color.yellow); setPreferredSize(new Dimension(200, 300)); addComponentListener(this); boules = new Boule[nBoules]; dim = getPreferredSize(); for (int k= 0; k < nBoules; k++) boules[k] = new Boule(dim); } public void run() {...} }
185
Master ISIDIS185 JSplitPane : exemple (5) Runnable public void run() { for(;;) { for (int k = 0; k < boules.length; k++) boules[k].move(dim); repaint(); if (sommeil != 0) { try { Thread.sleep(sommeil); } catch(InterruptedException e) {} } public void paintComponent(Graphics g) { g.setColor(getBackground()); g.fillRect(0, 0, dim.width, dim.height); for (int k = 0; k < boules.length; k++) g. drawImage(img,(int) boules[k].x,(int) boules[k].y, this); }
186
Master ISIDIS186 JSplitPane : exemple (6) ComponentListener public void componentHidden(ComponentEvent e){} public void componentShown(ComponentEvent e){} public void componentMoved(ComponentEvent e){} public void componentResized(ComponentEvent e){ dim = getSize(); for (int k = 0; k < boules.length; k++) boules[k].moveIntoRect(dim); }
187
Master ISIDIS187 JSplitPane : exemple (7) Les boules class Boule { protected double x, y, vx, vy; public Boule(Dimension dim) { x = dim.width * Math.random(); y = dim.height * Math.random(); double angle = 2 * Math.PI * Math.random(); vx = 2* Math.cos(angle); vy = 2* Math.sin(angle); } public void move(Dimension dim) { double nx =x +vx; double ny =y +vy; if ((nx dim.width)) { vx = - vx; nx = x +vx; } if ((ny dim.height)) { vy = - vy; ny = y +vy; } x = nx; y = ny; } public void moveIntoRect(Dimension dim) { x = Math.max(x, 0); x = Math.min(x, dim.width); y = Math.max(y, 0); y = Math.min(y, dim.height);
188
Master ISIDIS188 JTree Description hiérarchique de données Sept autre classes utilisées TreeModel : contient les données figurant dans l’arbre TreeNode : implémentation des nœuds et de la structure d’arbre TreeSelectionModel : contient le ou les nœuds sélectionnés TreePath : un tel objet contient un chemin (de la racine vers le sommet sélectionné par exemple) TreeCellRenderer : est appelé pour dessiner un nœud TreeCellEditor : l’éditeur pour un nœud est éditable TreeUI : look-and-feel
189
Master ISIDIS189 JTree Un arbre est créé à partir d’un TreeModel Il existe plusieurs modèles de sélection sélection d’un seul élément sélection de plusieurs éléments contigus sélection de plusieurs éléments disparates On peut indiquer un CellRenderer pour afficher une cellule de façon particulière. On peut indiquer un CellEditor pour changer la valeur d’une cellule interface TreeModel {... public Object getChild(Object parent, int index); public Object getRoot(); public boolean isLeaf(Object node);... }
190
Master ISIDIS190 JTree JTree fournit une vue du modèle Le modèle d’arbre est en deux étapes: Le modèle des nœuds est en trois étages: Constructeurs une feuille peut recevoir des fils ? reste sans fils ? interface TreeModel class DefaultTreeModel implements TreeModel interface TreeNode interface MutableTreeNode extends TreeNode class DefaultMutableTreeNode implements MutableTreeNode JTree() JTree(TreeNode racine) JTree(TreeNode racine, boolean enfantsPermis) JTree(TreeModel modele) JTree(TreeModel modele, boolean enfantsPermis)
191
Master ISIDIS191 Jtree : exemple class Arbre extends JPanel { JTree tree; public Arbre() { DefaultMutableTreeNode top, noeud, fils, n; top = new DefaultMutableTreeNode("Top"); tree = new JTree(top); noeud = new DefaultMutableTreeNode("Repertoire 1"); top.add(noeud); n = new DefaultMutableTreeNode("1a"); noeud.add(n); n = new DefaultMutableTreeNode("1b"); noeud.add(n);... noeud = new DefaultMutableTreeNode("Repertoire 2"); top.add(noeud); n = new DefaultMutableTreeNode("2a"); noeud.add(n);.... fils = new DefaultMutableTreeNode("2d"); noeud.add(fils); n = new DefaultMutableTreeNode("3a"); fils.add( n); }... }
192
Master ISIDIS192 JTree Le contenu d’un nœud est appelé user object C’est un objet A l’affichage, la méthode toString() d’un nœud délègue à la méthode toString() du contenu.
193
Master ISIDIS193 JTree Un DefaultTreeCellRenderer s’occupe du rendu. Il peut être modifié par des fonctions utilitaires par une redéfinition DefaultTreeCellRenderer rendu ; rendu = (DefaultTreeCellRenderer) tree.getCellRenderer(); rendu.setOpenIcon(new ImageIcon("Opened.gif")); rendu.setLeafIcon(new ImageIcon("Leaf.gif"));
194
Master ISIDIS194 JTree Un TreeSelectionListener rapporte tous les changements dans les sélections De nombreuses fonctions utilitaires tree.addTreeSelectionListener(new Selecteur()); class Selecteur implements TreeSelectionListener { public void valueChanged(TreeSelectionEvent e ) { message.setText("Nouveau : " + e.getNewLeadSelectionPath()); }
195
Master ISIDIS195 JTree On parcourt un arbre par une énumération Il en existe quatre : breadthFirstEnumeration depthFirstEnumeration postorderEnumeration preorderEnumeration public void actionPerformed(ActionEvent ev) { DefaultMutableTreeNode n, top; Enumeration e; top = (DefaultMutableTreeNode) tree.getModel().getRoot(); System.out.println("\ n En largeur"); e =top.breadthFirstEnumeration(); while (e.hasMoreElements()) { n = (DefaultMutableTreeNode) e.nextElement(); System.out.println(n.getUserObject() + " "); }
196
Master ISIDIS196 JTree : exemple(1) Dans cette application, on entre un nom de classe dans la zone de texte, et la classe s’insère dans la hiérarchie des classes. La classe Class permet de connaître la classe mère. On n’insère une classe que si elle n’est pas déjà dans l’arbre.
197
Master ISIDIS197 JTree : exemple(2) C’est addClass(Class c) qui fait l’insertion class ClassTreeFrame extends JFrame implements ActionListener { private DefaultMutableTreeNode root; private DefaultTreeModel model; private JTree tree; private JTextField textField; public ClassTreeFrame() { setTitle("ClassTree"); root = new DefaultMutableTreeNode(Object.class); model = new DefaultTreeModel(root); tree = new JTree(model); addClass( getClass()); getContentPane().add(new JScrollPane(tree), "Center"); textField = new JTextField(); textField.addActionListener(this); getContentPane().add(textField, "South"); }... }
198
Master ISIDIS198 JTree : exemple(3) public DefaultMutableTreeNode addClass(Class c) { if (c.isInterface() || c.isPrimitive()) return null; //pas les interfaces findUserObject(c) //cherche c dans tree DefaultMutableTreeNode node = findUserObject(c); if (node != null) return node; Class s = c.getSuperclass(); //classe mère DefaultMutableTreeNode parent = addClass(s); //appel récursif DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(c); model.insertNodeInto(newNode, parent, parent.getChildCount()); //à la fin //développe l’arbre pour que le nœud soit visible TreePath path = new TreePath(model.getPathToRoot(newNode)); tree.makeVisible(path); return newNode; }
199
Master ISIDIS199 JTree : exemple(4) Trouver un nœud dans un arbre Un simple parcours, en largeur par exemple public DefaultMutableTreeNode findUserObject(Object obj){ Enumeration e = root.breadthFirstEnumeration(); while (e.hasMoreElements()) { DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.nextElement(); if (node.getUserObject().equals(obj)) return node; } return null; }
200
Master ISIDIS200 JTree : exemple(5) Lire le nom de la classe On fait confiance à Java… public void actionPerformed(ActionEvent event) { String text = textField.getText(); try { Class c = Class.forName(text); //essayons addClass(c); textField.setText(""); } catch (ClassNotFoundException e) { Toolkit.getDefaultToolkit().beep(); //si la classe n’existe pas }
201
Master ISIDIS201 JTable JTable affiche des données dans un tableau TableModel régit la gestion des données On peut fournir les données dans un tableau bidimensionnel d’objets : Object[][] et utiliser le DefaultTableModel, mais il vaut mieux étendre AbstractTableModel. La sélection est régi par une modèle de sélection De plus, il y a un modèle de colonnes. Un tableau est entouré d’ascenseurs, en général.
202
Master ISIDIS202 JTable Les constructeurs sont : JTable() modèles par défaut pour les trois modèles JTable(int numRows, int numColumns) avec autant de cellules vides JTable(Object[][] rowData, Object[] columnNames) avec les valeurs des cellules de rowData et noms de colonnes columnNames. JTable(TableModel dm) avec le modèle de données dm, les autres par défaut. JTable(TableModel dm, TableColumnModel cm) avec modèle de données et modèle de colonnes fournis. JTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) Les trois modèles sont fournis. JTable(Vector rowData, Vector columnNames) ici, les données sont fournies par colonne.
203
Master ISIDIS203 JTable : exemple class TablePlanetes extends JPanel { TablePlanetes() { setLayout(new BorderLayout()); JTable table = new JTable(cellules, columnNames); add(new JScrollPane(table), BorderLayout.CENTER); } private Object[][] cellules = { { "Mercure", new Double(2440), new Integer(0), "non"}, { "Vénus", new Double(6052), new Integer(0), "non"}, { "Terre", new Double(6378), new Integer(1), "non"}, { "Mars", new Double(3397), new Integer(2), "non"}, { "Jupiter", new Double(71492), new Integer(16), "oui"}, { "Saturne", new Double(60268), new Integer(18), "oui"}, { "Uranus", new Double(25559), new Integer(17), "oui"}, { "Neptune", new Double(24766), new Integer(8), "oui"}, { "Pluton", new Double(1137), new Integer(1), "non"} }; private String[] columnNames = { "Planète", "Rayon", "Lunes", "Gazeuse"};
204
Master ISIDIS204 JTable Les données sont accessible par un modèle. Ils peuvent être stockés ou calculés, de façon transparente. La classe AbstractTableModel implémente les méthodes d’un modèle de table, sauf qui retournent respectivement le nombre de lignes le nombre de colonnes l’objet à afficher dans les ligne et colonne indiquées (sa méthode toString est utilisée). public int getRowCount() public int getColumnCount() public Object getValueAt(int ligne, int colonne)
205
Master ISIDIS205 JTable : un premier exemple En plus des trois méthodes obligées, la fonction getColumClass a été redéfinie, ce qui produit la justification à droite class SimpleTable extends JPanel { SimpleTable() { setLayout(new BorderLayout()); TableModel dataModel = new AbstractTableModel() { public int getColumnCount() { return 10; } public int getRowCount() { return 10;} public Object getValueAt(int row, int col) { return new Integer((1 + row)*(1 + col)); } public Class getColumnClass(int column) { return Number.class; } }; JTable table = new JTable(dataModel); add(new JScrollPane(table)); }
206
Master ISIDIS206 JTable : un deuxième exemple Construction par avec bien entendu méthodes à écrire (plus getColumnName qui, par défaut, numérote A, B, etc.): TableModel model = new ModelInvestment(30, 5, 10); JTable table = new JTable(model); class ModelInvestment extends AbstractTableModel {...} public int getRowCount() public int getColumnCount() public Object getValueAt(int ligne, int colonne) public String getColumnName(int colonne)
207
Master ISIDIS207 JTable : détails class ModelInvestment extends AbstractTableModel { private int annees; private int tauxMin; private int tauxMax; private static double depot = 100000.0; ModelInvestment(int annees, int tauxMin, int tauxMax) { this.annees = annees; this.tauxMin = tauxMin; this.tauxMax = tauxMax; } public int getRowCount() { return annees;} public int getColumnCount() { return tauxMax - tauxMin + 1;} public Object getValueAt(int ligne, int colonne) { double taux = (colonne + tauxMin) / 100.0; double depotFinal = depot * Math.pow(1 + taux, ligne); return NumberFormat.getCurrencyInstance().format(depotFinal); } public String getColumnName(int colonne) { double taux = (colonne + tauxMin) / 100.0; return NumberFormat.getPercentInstance().format(taux); }
208
Master ISIDIS208 JTable La classe abstraite java.text.NumberFormat est la classe de base pour le formatage de nombres. Nombreuses méthodes statiques retournant des formats appropriés. Le formatage effectif se fait par la méthode du format Exemples de méthodes: Le format dépend de la Locale, c’est-à-dire du pays concerné. NumberFormat.getNumberInstance() NumberFormat.getCurrencyInstance() NumberFormat.getPercentInstance() String format(int donnée)
209
Master ISIDIS209 JTable La méthode boolean TableModel.isCellEditable(int l, int c) renvoie true si la cellule peut être modifiée (par défaut non) La méthode int JTable.columnAtPoint(Point p) renvoie l’indice de la colonne du tableau où est le point La méthode int JTable.convertColumnIndexToModel(int colonne) renvoie l’indice, dans le modèle, de l’indice colonne
210
Master ISIDIS210 JTable : tri par ligne (1) On veut trier les lignes, en fonction d’une colonne désignée par un double clic. On veut que le tri se fasse sur la vue, pas sur le modèle, pour que le modèle soit inchangé. Pour cela, on introduit un filtre de modèle similaire aux filtres de streams. Ce filtre enregistre une référence au modèle réel, et intercepte les communications entre la table et son modèle pour les réinterpréter. Le filtre maintient une permutation des lignes déterminée par le tri virtuel des lignes en fonction de la colonne choisie.
211
Master ISIDIS211 JTable : tri par ligne (2) Le modèle est associé à la table class TablePlanetes extends JPanel { TablePlanetes() { setLayout(new BorderLayout()); DefaultTableModel model = new DefaultTableModel(cellules, columnNames); FiltreTriModel sorter = new FiltreTriModel(model); JTable table = new JTable(sorter); sorter.addEcouteur(table); add(new JScrollPane(table), BorderLayout.CENTER); } private Object[][] cellules ={... }; private String[] columnNames = {... }; }
212
Master ISIDIS212 JTable : tri par ligne (3) La classe FiltreTriModel est un modèle de table avec une référence au modèle réel un tableau de lignes une méthode de tri de ce tableau Le tri est fait par la méthode statique de la classe Arrays, en fonction de la colonne choisie. Pour cela, la classe Ligne doit implémenter l’interface Comparable. class FiltreTriModel extends AbstractTableModel { public FiltreTriModel(TableModel m) { model = m; lignes = new Ligne[model.getRowCount()]; for (int i = 0; i < lignes.length; i++) { lignes[i] = new Ligne(); lignes[i].index = i; } public void sort(int c) { colonneTri = c; Arrays.sort(lignes); fireTableDataChanged(); }... private TableModel model; private int colonneTri; private Ligne[] lignes; }
213
Master ISIDIS213 JTable : tri par ligne (4) La classe Ligne est interne à FiltreTriModel pour accéder facilement aux données. Une ligne est plus petite qu’une autre si son élément dans la colonne de tri est plus petit que l’élément de cette même colonne dans l’autre ligne. class FiltreTriModel extends AbstractTableModel {... private class Ligne implements Comparable { public int index; public int compareTo(Object autre) { Ligne autreLigne = (Ligne) autre; Object cellule = model.getValueAt(index, colonneTri); Object autreCellule = model.getValueAt(autreLigne.index, colonneTri); return ((Comparable) cellule).compareTo(autreCellule); }
214
Master ISIDIS214 JTable : tri par ligne (5) Les trois fonctions obligées tiennent compte de l’ordre virtuel class FiltreTriModel extends AbstractTableModel {... public Object getValueAt(int r, int c) { return model.getValueAt(lignes[r].index, c); } public int getRowCount(){ return model.getRowCount();} public int getColumnCount(){ return model.getColumnCount();} public String getColumnName(int c){ return model.getColumnName(c);} public Class getColumnClass(int c){ return (c == 1 || c == 2) ? Number.class : Object.class; }
215
Master ISIDIS215 JTable : tri par ligne (6) Et le double clic C’est l’en-tête de colonne qui réagit. Au double clic sur une colonne, on récupère le numéro de la colonne dans le modèle class FiltreTriModel extends AbstractTableModel {... public void addEcouteur(final JTable table) { table.getTableHeader().addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent event) { if (event.getClickCount() < 2) return; int tc = table.columnAtPoint(event.getPoint()); int mc = table.convertColumnIndexToModel(tc); sort(mc); } }); }
216
Master ISIDIS216 JTable : tri par ligne (7) Événements La classe JTable écoute les événements reçus du modèle Le modèle a plusieurs méthodes pour signaler des modifications de données Les colonnes sont régies par un TableColumnModel qui a ses propres notificateurs d’événements fireTableDataChanged() fireTableStructureChanged() fireTableRowsInverted(int first, int last) fireTableRowsUpdated(int first, int last) fireTableRowsDeleted(int first, int last) fireTableCellUpdated(int row, int col) fireTableChangedEvent(TableModelEvent e)
217
Master ISIDIS217 Undo/Redo Le « undo » (annuler) et « redo » (refaire) sont parmi les opérations les plus appréciées dans les interfaces ergonomiques. Ce sont des opérations difficiles à implémenter. Questions: quelles sont les transactions annulables ? quelle partie de l’environnement doit être sauvegardée pour pouvoir le reconstituer ? vaut- il mieux conserver l’opération, ou son inverse ? Java fournit un cadre surtout adapté aux opérations sur les textes. Plusieurs variantes existent, mais il reste du travail au programmeur.
218
Master ISIDIS218 UndoableEdit L’interface de base est UndoableEdit. Une implémentation par défaut est AbstractUndoableEdit « Edit » est synonyme de transaction ou opération, terme emprunté aux éditeurs de textes. Les méthodes sont boolean canUndo() indique que la transaction peut être annulée boolean canRedo() indique que la transaction peut être refaite void die() la transaction ne peut plus être annulée ni répétée void redo() throws CannotRedoException refait la transaction void undo() throws CannotUndoException annule la transaction
219
Master ISIDIS219 AbstractUndoableEdit C’est l’implémentation par défaut de UndoableEdit Elle maintient deux booléens internes alive et done qui gèrent correctement le canUndo() et canRedo(). On sous-classe cette classe en redéfinissant undo() et redo(). On utilise la super-classe en appelant super. undo(), super.redo().
220
Master ISIDIS220 Exemple des boutons à cocher L’opération de coche ou décoche peut être annulée ou refaite, à partir d’un autre composant (paire de boutons, plus souvent entrée de menu ou boutons d’une barre d’outils) Démarche: Chaque action sur le bouton génère un objet d’une classe ToggleEdit dérivant de AbstractUndoableEdit. L’objet contient le bouton concerné l’état du bouton La classe ToggleEdit redéfinit les méthodes undo() et redo(). L’opération d’annulation ou répétition est lancée en appelant la méthode undo() ou redo() sur l’objet créé.
221
Master ISIDIS221 ToggleEdit import javax.swing.undo.*; public class ToggleEdit extends AbstractUndoableEdit { private final JToggleButton bouton; private final boolean selectionne; public ToggleEdit(JToggleButton bouton) { this.bouton = bouton; selectionne = bouton.isSelected(); } public void redo() throws CannotRedoException { super.redo(); bouton.setSelected(selectionne); } public void undo() throws CannotUndoException { super.undo(); bouton.setSelected(!selectionne); }
222
Master ISIDIS222 Le panneau Le panneau est composé de trois boutons à cocher et de deux boutons d’annulation et répétition: chaque bouton à cocher ( JCheckBox ) a un écouteur dont la méthode actionPerformed est : JCheckBox gras = new JCheckBox("gras"); JCheckBox ital = new JCheckBox("italique"); JCheckBox soul = new JCheckBox("souligné"); JButton undoButton = new JButton("Undo"); JButton redoButton = new JButton("Redo"); undoButton.addActionListener(new UndoIt()); redoButton.addActionListener(new RedoIt()); public void actionPerformed(ActionEvent ev) { JToggleButton b = (JToggleButton) ev.getSource(); edit = new ToggleEdit(b); updateButtons(); }
223
Master ISIDIS223 Les listeners class UndoIt implements ActionListener { public void actionPerformed(ActionEvent ev) { try { edit.undo(); } catch (CannotUndoException ex) {} finally { updateButtons(); } class RedoIt implements ActionListener { public void actionPerformed(ActionEvent ev) { try { edit.redo(); } catch (CannotRedoException ex) {} finally { updateButtons(); } private void updateButtons() { undoButton.setText(edit.getUndoPresentationName()); redoButton.setText(edit.getRedoPresentationName()); undoButton.setEnabled(edit.canUndo()); redoButton.setEnabled(edit.canRedo()); }
224
Master ISIDIS224 Complément L’interface UndoableEdit a une méthode getPresentationName qui retourne une chaîne de caractère modifiable en fonction de la transaction Les méthodes getUndoPresentationName et getRedoPresentationName concatènent le préfix Undo et Redo avec la chaîne fournie par getPresentationName class ToggleEdit { private final JToggleButton bouton; private final boolean selectionne;... public String getPresentationName() { return "\"" + bouton.getText() + (selectionne ? " on" : " off") + "\""; }...
225
Master ISIDIS225 Séquences de transactions Pour se « souvenir » d’une séquence de transactions, et pouvoir revenir en arrière arbitrairement loin, on utilise un gestionnaire de transactions ( UndoManager ). Un UndoManager gère les transactions ( Edit ). Il permet de reculer ( undo ) et d’avancer ( redo ) tant que possible. Une transaction à inscrire dans un gestionnaire doit lui être notifiée, soit directement, par addEdit(UndoableEdit edit) soit en utilisant le fait qu’un UndoManager implémente un UndoableEditListener. On enregistre le gestionnaire dans la liste des auditeurs.
226
Master ISIDIS226 Implémentation simple Un UndoManager étend CompoundEdit qui lui étend AbstractUndoableEdit On remplace simplement la variable UndoEdit edit par UndoManager manager et on modifie actionPerformed() en conséquence
227
Master ISIDIS227 Modifications class TogglePanel extends Jpanel implements ActionListener { private UndoManager manager = new UndoManager(); private JButton undoButton,...; public void actionPerformed(ActionEvent e) { JToggleButton b = (JToggleButton) e.getSource(); manager.addEdit(new ToggleEdit(b)); updateButtons(); } class UndoIt implements ActionListener { public void actionPerformed(ActionEvent e){ try { manager.undo(); }... } private void updateButtons() { undoButton.setText(manager.getUndoPresentationName());... }
228
Master ISIDIS228 Un deuxième exemple Le programme de départ affiche, au clic de souris, un carré ou un cercle, selon que la touche majuscule n’est pas ou est enfoncé. La séquence des formes engendrées est enregistrée dans un vecteur en vue d’un affichage facile. Comment l’adapter au undo/redo ? class SimplePaint extends JPanel { protected Vector formes = new Vector(); protected PaintCanvas canvas =new PaintCanvas(formes); protected int width = 50; protected int height = 50; public SimplePaint() { setLayout(new BorderLayout()); add(new Label("Do it", Label.CENTER), BorderLayout.NORTH); add(canvas, BorderLayout.CENTER); canvas.addMouseListener(new AjouterForme()); }... }
229
Master ISIDIS229 Les formes et le canvas class AjouterForme extends MouseAdapter { public void mousePressed(MouseEvent e) { Shape s; if (e.isShiftDown()) s = new Ellipse2D.Double(e.getX(), e.getY(), width, height); else s = new Rectangle2D.Double(e.getX(), e.getY(), width, height); formes.addElement(s); canvas.repaint(); } class PaintCanvas extends JPanel { Vector formes;... public void paintComponent(Graphics g) { Graphics2D g2 = (Graphics2D) g; super.paintComponent(g2); g2.setColor(Color.black); Enumeration enum = formes.elements(); while (enum.hasMoreElements()) { Shape shape = (Shape) enum.nextElement(); g2.draw(shape); }
230
Master ISIDIS230 Ajouter undo/redo (1) Comme pour l’exemple précédent deux boutons « undo » et « redo » deux listeners d’actions, un sur chaque bouton Création d’une classe FormeEdit pour les transactions, et de deux classes dérivées.
231
Master ISIDIS231 Ajouter undo/redo (2) class FormeEdit extends AbstractUndoableEdit { protected Vector formes; protected Shape shape; public FormeEdit(Shape shape, Vector formes) { this.formes = formes; this.shape = shape; } public void undo() { super.undo(); formes.remove(shape); } public void redo() { super.redo(); formes.add(shape); } class CarreEdit extends FormeEdit { public CarreEdit(Shape s, Vector v) { super(s, v); } public String getPresentationName() { return "carré"; }
232
Master ISIDIS232 AjouterForme public void mousePressed(MouseEvent e) { Shape shape; UndoableEdit edit; if (e.isShiftDown()) { shape = new Ellipse2D.Double(e.getX(), e.getY(), width, height); edit = new CercleEdit(shape, formes); } else { shape = new Rectangle2D.Double(e.getX(), e.getY(), width, height); edit = new CarreEdit(shape, formes); } formes.addElement(shape); manager.addEdit(edit); canvas.repaint(); updateButtons(); }
233
Master ISIDIS233 Événements Il existe une classe spécifique UndoableEditEvent Un tel événement comporte une source, et un UndoableEdit Les auditeurs sont de l’interface UndoableEditListener, avec la méthode undoableEditHappened(UndoableEditEvent e). UndoManager implémente UndoableEditListener, avec la méthode public void undoableEditHappened( UndoableEditEvent e){ addEdit( e.getEdit()) } Usage :... UndoableEdit edit;... edit = new CercleEdit(shape, formes); UndoableEditEvent ue; ue = new UndoableEditEvent(this, edit);... manager. undoableEditHappened(ue);...
234
Master ISIDIS234 Mais… Il manque une objet qui lance des UndoableEditEvent ’s. L’implémentation précédente fait comme si, la présente le fait. Seuls les documents de textes sont capables, pour l’instant, d’en lancer. Lançons cela pour JPanel : class PaintCanvas extends JPanel {... Class uel = UndoableEditListener.class; public void addUndoableEditListener(UndoableEditListener listener) { listenerList.add(uel, listener); } public void removeUndoableEditListener(UndoableEditListener listener) { listenerList.remove(uel, listener); } public void fireUndoableEditUpdate(UndoableEditEvent e) { Object[] l = listenerList.getListenerList(); for (int i = l.length - 2; i >= 0; i -= 2) { if (l[i] == uel ) ((UndoableEditListener) l[i + 1]).undoableEditHappened(e); }
235
Master ISIDIS235 …et donc Un UndoManager est un listener parfait Il ne reste plus qu’à lancer les événements public FireUndo() {... canvas.addMouseListener(new AjouterForme()); canvas.addUndoableEditListener(manager); } public void mousePressed(MouseEvent e) { Shape shape; UndoableEdit edit; shape =... edit =... formes.addElement(shape); UndoableEditEvent ue = new UndoableEditEvent(this, edit); canvas.fireUndoableEditUpdate(ue); canvas.repaint(); updateButtons(); }
236
Master ISIDIS236 UndoableEditSupport Pour faciliter la vie aux programmeurs (en attendant que les choses se simplifient), Java propose une classe utilitaire de gestion de listeners et d’envoi d’événements, les UndoableEditSupport. Ils réalisent pour l’essentiel ce qui a été programmé en dur. class PaintCanvas extends JPanel { UndoableEditSupport support = new UndoableEditSupport();... public void addUndoableEditListener(UndoableEditListener listener) { support.addUndoableEditListener(listener); } public void removeUndoableEditListener(UndoableEditListener listener) { support.removeUndoableEditListener(listener); } public void postEdit(UndoableEdit e) {// le fireUndoableEditUpdate.... support.postEdit(e); }
237
Master ISIDIS237 UndoableEditSupport (fin) Au lieu de lancer les événements, on poste les Edit : public void mousePressed(MouseEvent e) { UndoableEdit edit; edit =... canvas.postEdit(edit);... }
238
Master ISIDIS238 Dans les textes Dans les textes, ça va tout seul. JTextArea editor = new JTextArea(); public UndoRedoText() { editor.getDocument().addUndoableEditListener(new ManageIt()); undoButton.addActionListener(new UndoIt()); redoButton.addActionListener(new RedoIt()); } class ManageIt implements UndoableEditListener { public void undoableEditHappened(UndoableEditEvent e) { manager.undoableEditHappened( e); updateButtons(); }
239
Master ISIDIS239 États Dans les exemples précédents, on conservait explicitement l’état après modification. Un tel procédé n’est pas suffisant dans de nombreuses situations, comme dans un groupe de boutons radio. Java propose une forme générale d’état appelé StateEdit. Tout objet dont la classe implémente l’interface StateEditable peut sauvegarder son état avant et après modification dans l’état, et ainsi le récupérer. Mieux, la prise en compte de l’état de départ et de l’état d’arrivée peut être programmée, permettant ainsi de cumuler des modifications.
240
Master ISIDIS240 États : description Un StateEdit est créé par La classe de unObjet implémente l’interface StateEditable. Un StateEdit contient en interne une table de hachage (en fait deux). Les méthodes de StateEditable permettent de sauvegarder et de récupérer les données à conserver. La sauvegarde débute à la création, et s’arrête par la méthode end() de StateEdit. StateEdit etat = new StateEdit(unObjet); public void storeState( Hashtable h); public void restoreState( Hashtable h);
241
Master ISIDIS241 États : structure de l’exemple class TogglePanel extends JPanel implements ActionListener, StateEditable { StateEdit etat; JButton undoButton, redoButton, chooseButton, endButton; JRadioButton gras, ital, soul; ButtonGroup polices; public TogglePanel() { // installer les composants; chooseButton.addActionListener(new ChooseIt()); endButton.addActionListener(new EndIt()); undoButton.addActionListener(new UndoIt()); redoButton.addActionListener(new RedoIt()); } public void storeState(Hashtable h) {...} public void restoreState(Hashtable h) {...} class ChooseIt implements ActionListener {...} class EndIt implements ActionListener {...} class UndoIt implements ActionListener {...} class RedoIt implements ActionListener {...} }
242
Master ISIDIS242 États : fin de l’exemple Trois boutons radio sont donnés. A partir de Choose, on démarre l’enregistrement des modifications, jusqu’à l’activation du bouton Ok. Un Undo restitue l’état initial, et un Redo revient à l’état final. class ChooseIt implements ActionListener { public void actionPerformed(ActionEvent ev) { etat = new StateEdit(TogglePanel.this); updateButtons(); } public void storeState(Hashtable h) { h. put(polices, polices.getSelection()); } public void restoreState(Hashtable h) { ButtonModel b = (ButtonModel) h.get(polices); b.setSelected(true); } class UndoIt implements ActionListener { public void actionPerformed(ActionEvent ev) { try { etat.undo(); } catch (CannotUndoException ex) {} updateB(); }
243
Master ISIDIS243 Le squelette d’un éditeur de texte Objectifs Présenter des interactions d’un composant de texte avec son environnement Ouverture et sauvegarde des fichiers Couper-coller Undo-Redo L’important est la cohérence de l’environnement Entrées des menus activables seulement si cela a un sens « Aide implicite » que cela apporte En revanche, on ignore le style du texte lui- même Style des paragraphes Polices de caractères
244
Master ISIDIS244 Cahier des charges Éditeur SDI (single document interface) un seul document présent une seule fenêtre de manipulation du texte Autres modèles une seule fenêtre, plusieurs documents plusieurs fenêtres, une par document Commandes de manipulation de documents nouveau, ouvrir, sauver, sauver sous Commandes de manipulation du texte copier-couper, coller, tout sélectionner annuler-rétablir Présentation de ces commandes sous forme menu - toolbar - raccourcis clavier
245
Master ISIDIS245 Textes et documents Classes de textes Classes de documents | +-- javax.swing JComponent | +-- javax.swing.text.JTextComponent | +-- javax.swing.JTextArea | +-- javax.swing.JTextField | +-- javax.swing.JEditorPane | +-- javax.swing.JTextPane java. lang. Object | +-- javax.swing.text.AbstractDocument implements Document | +-- javax.swing.text.PlainDocument | extends | +-- javax.swing.text.DefaultStyledDocument implements StyledDocument | +-- javax.swing.text.html.HTMLDocument
246
Master ISIDIS246 Document / Vue Un composant de texte présente une vue d’un document. TextArea et TextField associés au PlainDocument TextPane associé à StyledDocument Correspondance JTextArea editor; Document document = editor.getDocument(); editor.setDocument(new PlainDocument());
247
Master ISIDIS247 Écouter le texte Via le document, insertion, suppression, remplacement Un DocumentListener implémente trois méthodes appelées après modification d’un attribut, insertion, suppression. Document document = editor.getDocument(); document.addDocumentListener( un listener ); public void changedUpdate (DocumentEvent e); public void insertUpdate (DocumentEvent e); public void removeUpdate (DocumentEvent e);
248
Master ISIDIS248 Sélection On peut pister les déplacements du point d’insertion ( caret ) par un CaretListener Un CaretListener possède une méthode caretUpdate appelée chaque fois que le point d’insertion bouge Un CaretEvent fournit deux méthodes getDot() qui donne le point actuel getMark() qui donne le point précédent Un mouvement de souris, avec bouton enfoncé, ne provoque pas d’évènement, mais provoque un évènement quand on relâche le bouton Caret caret = editor.getCaret(); caret.addCaretListener( un CaretListener ); public void caretUpdate( CaretEvent e) { int now = e. getDot(); int before = e. getMark(); boolean nowSelected = now != before;... }
249
Master ISIDIS249 Manipulations de texte Manipulations de texte prédéfinies (sont en fait des méthodes de JTextComponent ) : les dernières transfèrent dans le presse- papier système. Le DefaultEditorKit prédéfinit une trentaine d’actions sur les composants de textes. void editor.cut(); void editor.copy(); void editor.paste(); void editor.selectAll(); Clipboard clip = Toolkit. getDefaultToolkit(). getSystemClipboard();
250
Master ISIDIS250 Vue d’ensemble Le texte une seule zone de texte ( JTextArea ) le document associé ne change pas, sauf pour la commande « nouveau ». Les actions chaque action ( Action ) (nouveau,…, tout sélectionner) est implémentée dans une classe séparée Les menus et la barre d’outils construits à partir des actions Les gestionnaires de cohérence de la cohérence des menu : une EditMenuManager de la cohérence des undo-redo : un UndoHandler de sauvegarde de documents modifiés : une StatusBar.
251
Master ISIDIS251 Composants Composants de la vue Composants de la gestion JTextComponent editor; JMenuBar menubar; JToolBar toolbar; StatusBar status; File currentFile = null; JFileChooser selecteurFichier; UndoHandler undoHandler; EditMenuManager editMenuManager;
252
Master ISIDIS252 Les Actions Une action… par action ! Action undoAction = new UndoAction(); Action redoAction = new RedoAction(); Action newAction = new NewAction(); Action openAction = new OpenAction(); Action saveAction = new SaveAction(); Action saveAsAction = new SaveAsAction(); Action exitAction = new ExitAction(); Action cutAction = new CutAction(); Action copyAction = new CopyAction(); Action pasteAction = new PasteAction(); Action selectAllAction = new SelectAllAction();
253
Master ISIDIS253 UndoAction Premier exemple : undoAction class UndoAction extends AbstractAction { public UndoAction() { super("Undo", new ImageIcon("gifs/undo.gif")); setEnabled(false); } public void actionPerformed(ActionEvent e) { try { undoHandler.undo(); } catch (CannotUndoException ex) {} undoHandler.update(); }
254
Master ISIDIS254 UndoHandler Il gère les undo, mais aussi l’état des boutons ! class UndoHandler extends UndoManager { public void undoableEditHappened(UndoableEditEvent e) { super.addEdit(e.getEdit()); update(); } public void update() { undoAction.setEnabled(canUndo()); redoAction.setEnabled(canRedo()); }
255
Master ISIDIS255 CutAction Couper implique mettre dans la corbeille mettre à jour les boutons class CutAction extends AbstractAction { CutAction() { super("Cut", new ImageIcon("gifs/cut.gif")); } public void actionPerformed(ActionEvent e) { getEditor().cut(); // texte editMenuManager.doCut(); // boutons }
256
Master ISIDIS256 EditMenuManager Il gère les transitions entre les 4 états du menu la mise-à-jour de la vue (menu et toolbar) par la fonction update() class EditMenuManager implements CaretListener { int state; static final int EMPTY = 0, CUTCOPY = 1, PASTE = 2, FULL = 3; void doInitial() {...} void doCopy() {...} void doCut() {...} void doPaste() {...} void doSelected() {...} void doDeselected() {...} }
257
Master ISIDIS257 EditMenuManager (suite) Après une sélection : Après un copy : void doSelected() { if (state == EMPTY) state = CUTCOPY; else if (state == PASTE) state = FULL; updateEnables(state); } void doCopy() { if (state == CUTCOPY) { state = FULL; updateEnables(state); }
258
Master ISIDIS258 EditMenuManager (suite) C’est aussi un CaretListener, pour écouter les sélections public void caretUpdate(CaretEvent e) { int now = e.getDot(); int before = e.getMark(); boolean nowSelected = now != before; if (nowSelected) doSelected(); else doDeselected(); }
259
Master ISIDIS259 EditMenuManager (fin) La mise- à- jour des boutons est paresseuse public void updateEnables(int state) { switch (state) { case EMPTY : cutAction.setEnabled(false); copyAction.setEnabled(false); pasteAction.setEnabled(false); break; case CUTCOPY: cutAction.setEnabled(true); copyAction.setEnabled(true); pasteAction.setEnabled(false); break; case PASTE:... case FULL:... }
260
Master ISIDIS260 Ouvrir un fichier Il faut s’assurer que le fichier courant n’est pas modifié s’il est modifié, demander une éventuelle sauvegarde ouvrir un dialogue de choix de fichier lire ce fichier Ces opérations sont assumées par la méthode actionPerformed() class OpenAction extends AbstractAction { OpenAction() { super("Ouvrir...", new ImageIcon("gifs/open. gif")); } public void actionPerformed(ActionEvent e) {...} }
261
Master ISIDIS261 Ouvrir un fichier (suite) public void actionPerformed(ActionEvent e) { if (!isConfirmed( "Voulez vous sauver le texte courant\ n"+ " avant d'ouvrir un autre fichier ?", "Sauver avant d'ouvrir ?")) return; int answer = selecteurFichier.showOpenDialog(frame); if (answer != JFileChooser.APPROVE_ OPTION) return; currentFile = selecteurFichier.getSelectedFile(); try { FileReader in = new FileReader(currentFile); getEditor().read(in, null); in.close(); } catch (IOException ex) { ex.printStackTrace(); } status.setSaved(); frame.setTitle(currentFile.getName()); }
262
Master ISIDIS262 Ouvrir un fichier (fin) boolean isConfirmed(String question, String titre) { if (! status.isModified()) return true; int reponse = JOptionPane.showConfirmDialog(null, question, titre, JOptionPane.YES_ NO_ CANCEL_ OPTION); switch(reponse) { case JOptionPane.YES_ OPTION:{ saveAction.actionPerformed(null); return !status.isModified(); } case JOptionPane.NO_ OPTION: return true; case JOptionPane.CANCEL_ OPTION: return false; } return false; }
263
Master ISIDIS263 État du document Il n’existe pas de fonction qui indique une modification du document StatusBar assume ce rôle… class StatusBar extends JPanel implements DocumentListener { boolean modStatus = false; // true = modified; public boolean isModified() { return modStatus; } public void changedUpdate(DocumentEvent ev) { setModified();} public void insertUpdate(DocumentEvent ev) { setModified();} public void removeUpdate(DocumentEvent ev) { setModified();} public void setSaved() { modStatus = false; getEditor().getDocument().addDocumentListener(this); saveAction.setEnabled(false); } public void setModified() { modStatus = true; getEditor().getDocument().removeDocumentListener(this); saveAction.setEnabled(true); }
264
Master ISIDIS264 Les menus Dans le menu « Fichier », on ajoute des raccourcis protected JMenuBar createMenubar() { JMenuBar mb = new JMenuBar(); JMenu menu; JMenuItem item; menu = new JMenu("Fichier"); item = menu. add(newAction); item.setIcon(null); item.setMnemonic('N'); item = menu. add(openAction); item.setIcon(null); item.setMnemonic('O');... menu.addSeparator(); item = menu.add(exitAction); mb. add(menu);... return mb; }
265
Master ISIDIS265 La barre d’outils On ajoute les tooltips, des espaces et de la glue private JToolBar createToolbar() { JButton b; JToolBar tb = new JToolBar(); b = tb.add(newAction); b.setText(null); b.setToolTipText("nouveau");... tb.add(Box.createHorizontalStrut(10)); b = tb.add(copyAction); b.setText(null); b.setToolTipText("copier");... tb.add(Box.createHorizontalGlue()); return tb; }
Présentations similaires
© 2024 SlidePlayer.fr Inc.
All rights reserved.