200 likes | 323 Views
Objectifs de ce cours (I21). Classes et objets simples Découpage structurel et fonctionnel Modularité fonctionnelle, robustesse et maintenance. Classes et objets simples sans méthode Méthodes d’instance simples Plus à propos de Java Classes et Objets ensemblistes
E N D
Objectifs de ce cours (I21) Classes et objets simples Découpage structurel et fonctionnel Modularité fonctionnelle, robustesse et maintenance • Classes et objets simples sans méthode • Méthodes d’instance simples • Plus à propos de Java • Classes et Objets ensemblistes • Classes encapsulant des tableaux d’objets Résumé du cours précédent Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde
Classes des cours et TD revisitées : - = private + = public $= static classe LigneDeJeu classe JeuDominos classe DonneJoueur classe Pioche // Méthodes d’instance +ligneDeJeuToString():String +addToRight(Domino d):boolean +addToLeft(Domino d):boolean ... // Variable d’instance -jeu:Domino[] // Variable d’instance -laDonne:Domino[] // Variable d’instance -ldj:Domino[] // Variable d’instance -laPioche:Domino[] // Cons. de ligne vide +LigneDeJeu() // Cons. de donne vide +DonneJoueur() // Cons. d’un jeu vide +JeuDominos() // Cons. de pioche vide +Pioche() // Méthodes d’instance +donneToString():String +add(Domino d):void +add(Domino[] setDominos):void +isEmpty():boolean +size():int +extractDomino(int rg):Domino +extractDomino(Domino d):Domino +joueUnDomino(Pioche pioche, LigneDeJeu ldj):boolean +getDominoMaxValueToPlay(LigneDeJeu ldj):Domino // Méthodes d’instance +createJeuNormal():void +jeuDominosToString():String +size():int // Méthodes d’instance +piocheToString():String +add(Domino d):void +add(Domino[] setDominos):void +isEmpty():boolean +size():int +piocheUnDomino():Domino Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde
(A) CLASSE ENCAPSULANT UN TABLEAU D’OBJETS Résumé Nous avons vu : ➽ comment concevoirdes classes qui encapsulent des tableaux d’objets(i.e. définir quelles méthodes d’instance nous seront utiles voire indispensables pour être en mesure de manipuler leurs objets le plus simplement possible depuis extérieur) ➽ comment écrireces classes(i.e. faire des choix d’implémentation ; par exemple, construction à vide en utilisant null) ➽ comment faire interagirles objets par une utilisation correcte de la délégation(i.e. doter les bons objets des bonnes méthodes en prenant soin de relayer les traitements à réaliser jusqu’aux objets qui savent le mieux les faire) ➽ comment assurer la protectionde l’état interne des objets depuis l’extérieur(i.e. interdire la visibilité directe des variables d’instance des objets en les déclarant private) Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde
Objectifs de ce cours (I21) Classes et objets simples Découpage structurel et fonctionnel Modularité fonctionnelle, robustesse et maintenance • Classes et objets simples sans méthode • Méthodes d’instance simples • Plus à propos de Java • Classes et Objets ensemblistes • Classes encapsulant des tableaux d’objets • Classes génériques à spécialiser Maintenant ! Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde
(B) CLASSES GÉNÉRIQUES À SPÉCIALISER Nous avons vu des classes qui encapsulent des tableaux d’objets mais, on constate : ➽ que ces classes sont complexes à écrire(i.e. principalement par rapport à la gestion dynamique des tableaux qui nécessite desré-instanciations des tableaux et des recopies de leurs éléments) ➽ que plusieurs méthodes identiques doivent être réécrites pour chaque classe(par exemple, c’est le cas des méthodes d’ajout, de suppression ou insertion d’éléments) Ces problèmes revenant souvent dans les programmes, les langages de programmations fournissent des types ou des librairies standards permettant de manipuler dynami-quement des ensembles d’éléments sans avoir à s’occuper des aspects statiques. En Java, on dispose notamment de classes génériques prêtes à être spécialisées. Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde
(B) CLASSES GÉNÉRIQUES À SPÉCIALISER:ArrayList<E> La classe générique ArrayList<E> ArrayList<E> doit être spécialisée à l’aide d’un nom de classe d’objets (ATTENTION: ArrayList<int> est interdit car int est un type primitif non objet) ➽ Exemples de spécialisation de la classe générique :ArrayList<Domino> représente, dès lors, la classe des listes de dominosArrayList<Livre> représente, dès lors, la classe des listes de livresArrayList<Integer> représente, dès lors, la classe des listes d’entiers ➽ si on veut les instancier, on peut utiliser un constructeur par défaut :new ArrayList<Domino>( )instancie une liste de dominos videnew ArrayList<Livre>( )instancie une liste de livres videnew ArrayList<Integer>( )instancie une liste d’entiers vide ➽ si on voulait les instancier tout en conservant leurs références, on pourrait faire :ArrayList<Domino> listeDominos = new ArrayList<Domino>( ) ArrayList<Livre> listeLivres = new ArrayList<Livre>( )ArrayList<Integer> listeEntiers = new ArrayList<Integer>( ) Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde
(B) CLASSES GÉNÉRIQUES À SPÉCIALISER:ArrayList<E> Une fois la classe générique spécialisée, on dispose d’une véritable classe qui : ➽ encapsule un ensemble d’objets :la classe ArrayList<E> encapsule un tableau d’objets et en gère les aspects dynamiques induits par l’ajout, l’insertion ou la suppression d’éléments ➽ dispose de nombreuses méthodes d’instance publiques :par rapport à l’ensemble size(), isEmpty()par rapport à la lecture dans l’ensembleget(...), contains(...)par rapport à l’écriture dans l’ensemble add(...), clear(), remove(...), set(...) ➽ considère un ordre naturel sur l’ensemble :celui défini par les indices des éléments du tableau encapsulé De plus, les méthodes ont été implémentées pour s’exécuter en un temps optimal : ➽ optimisation en temps constant(soit O(1)) :obtention de la taille, test si vide, obtention d’un élément, remplacement d’un élément ➽ optimisation en temps linéaire(soit O(n)) maisen temps constant amorti(soit O(1)):ajout, insertion, suppression (http://fr.wikipedia.org/wiki/Analyse_amortie) Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde
(B) CLASSES GÉNÉRIQUES À SPÉCIALISER:ArrayList<E> Comment la classe ArrayList<E> gère-t-elle son tableau d’objet ? ➽ elle prévoit à l’avance une possible augmentation du nombre d’éléments :- elle gère le nombre d’éléments valides ainsi qu’une réserve d’emplacements libres- lorsque la réserve est vide, elle la reconstruit- finalement, elle maintient un tableau qui n’est que rarement complètement rempli Éléments déjà présents Réserve Nouveaux éléments présents ajouts de 4 éléments Taille réelle du tableau (plein) Taille réelle du tableau ajout d’un nouvel élément Nouveaux éléments présents Nouvelle réserve L’ancien tableau est détruit après avoir été reconstruit et recopié Nouvelle taille réelle du tableau Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde
(B) CLASSES GÉNÉRIQUES À SPÉCIALISER:ArrayList<E> Méthodes utiles à connaître de la classe ArrayList<E> : ➽ par rapport à l’ensemble :int size() retourne la taille de l’ensembleboolean isEmpty() teste si l’ensemble est videObject clone() retourne une copie de l’objet ArrayList<E> ...du tableau seulement (pas des éléments eux-mêmes) ➽ par rapport à la lecture dans l’ensemble :E get(int index) retourne l’élément d’indice indexboolean contains(Object o) teste si l’ensemble contient l’objet oint indexOf(Object o) retourne l’indice de l’élément o ou -1 s’il n’existe pasint lastIndexOf(Object o) retourne l’indice du dernier élément o ou -1 s’il n’existe pas ➽ par rapport à l’écriture dans l’ensemble :boolean add(E e) ajoute un nouvel élément à la fin du tableauvoid add(int index, E e) insert un nouvel élément à l’indice indexE set(int index, E e) remplace l’élément d’indice index par e et retourne l’ancienE remove(int index) supprime l’élément d’indice index et le retourneboolean remove(Object o) supprime l’élément o et retourne true s’il existait, false sinon ...retourne toujours true Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde
(B) CLASSES GÉNÉRIQUES À SPÉCIALISER:ArrayList<E> Comment utiliser la classe ArrayList<E> ? En O-O, il existe 3 manières différentes : ①directement dans la classe où on se trouveOn instancie directement un objet ArrayList<Domino>.Puis on l’utilise à travers ses méthodes d’instance (celles des ArrayList<E>). ex: ArrayList<Domino> jeu = new ArrayList<Domino>(); jeu.add(new Domino(0,0)); ... Maintenant, il suffit d’appliquer les méthodes disponibles ②indirectement en l’encapsulant dans une nouvelle classeOn crée, par exemple, une classe JeuDominosqui encapsule un privateArrayList<Domino>.Mais alors, on ne peut pas accéder aux méthodes d’instance précédentes directement de l’extérieur d’un objet JeuDominos. ex: JeuDominos jeu = new JeuDominos(); jeu.add(new Domino(0,0)); // erreur : la méthode add(...) n’existe pasOn doit donc munir la classe JeuDominosde méthodes similaires Nous en verrons par la suite ③en créant une nouvelle classe qui hérite des propriétés de l’objet ArrayList<E> Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde Nous ne verrons cette méthode que plus tard
(B) CLASSES GÉNÉRIQUES À SPÉCIALISER:ArrayList<E> Quel est le meilleur emplacement pour faire jouer les joueurs tour à tour ? On prévoira une méthode comme : public boolean jouerUnCoup() qui fera jouer un joueur à son tour en utilisant une variable qui indique toujours à quel joueur c’est le tour de jouer. Méthode ① (directe) Méthode ② (indirecte) On envisage une nouvelle classe Joueurs Utilisation directe d’un ArrayList<E> classe JeuDominos classe JeuDominos // Var. instance privées -joueurs:Joueurs // Var. instance privées -joueurs:ArrayList<Joueur> -joueurQuiDoitJouer:int ① ① Encapsulation de l’ArrayList<E> dans la nouvelle classe Joueurs classe Joueurs // Var. instance privées -joueurs:ArrayList<Joueur> -joueurQuiDoitJouer:int Plus proche de l’utilisation de simples tableaux mais acceptable ici ! ② Plus proche du paradigme O-O (ce sont les joueurs qui savent qui doit jouer et quand) ➽ Le meilleur emplacement est celui où l’on décide de placer l'ensemble des joueurs Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde
(B) CLASSES GÉNÉRIQUES À SPÉCIALISER:ArrayList<E> Quelles sont les conséquences qui découlent du choix de méthode ? Méthode ① (directe) Méthode ② (indirecte) On doit placer la méthode dans la classe JeuDominos On doit placer la méthode dans la classe Joueurs... // Méthode d’instance +jouerUnCoup():boolean classe JeuDominos classe JeuDominos // Var. instance privées -joueurs:ArrayList<Joueur> -joueurQuiDoitJouer:int // Var. instance privées -joueurs:Joueurs // Méthode d’instance +jouerUnCoup():boolean ...mais, en plus, on devra prévoir qu’elle puisse être invoquée depuis la classe JeuDominos (seule visible de l’extérieur) ① ① Ici, c’est la classe JeuDominos qui doit supporter l’algorithmique qui permet de faire jouer le bon joueur au bon moment classe Joueurs // Var. instance privées -joueurs:ArrayList<Joueur> -joueurQuiDoitJouer:int // Méthode d’instance +jouerUnCoup():boolean ② ➽ La délégation a pour effet de réduire le problème global en le morcelant en plusieurs endroits (chaque partie du pb. devient plus clairecar placé à l’endroit le plus judicieux) Ici, la classe JeuDominos délègue à la classe Joueurs la connaissance et l’algorithmique qui permet de faire jouer le bon joueur au bon moment Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde
(B) CLASSES GÉNÉRIQUES À SPÉCIALISER:ArrayList<E> Quel est le meilleur emplacement pour faire piocher un domino ? On prévoira la méthode : public Domino piocher() qui retournera un domino non nul pioché aléatoirement quand la pioche n’est pas vide et nul sinon. Méthode ① (directe) Méthode ② (indirecte) On envisage une nouvelle classe Pioche Utilisation directe d’un ArrayList<E> classe JeuDominos classe JeuDominos // Var. instance privées -pioche:Pioche // Var. instance privées -pioche:ArrayList<Domino> ① ① Encapsulation de l’ArrayList<E> dans la nouvelle classe Pioche Déconseillé ici car le fait de piocher aléatoirement devient extérieur à l’objet pioche ! classe Pioche // Var. instance privées -pioche:ArrayList<Domino> ② Plus proche du paradigme O-O (seule la pioche maîtrise la manière de piocher) ➽ Le meilleur emplacement est toujours celui qui permet de garantir que des fonctionnalités importantes soient prises en charge par une classe spécifique (=> délégation) Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde
(B) CLASSES GÉNÉRIQUES À SPÉCIALISER:ArrayList<E> Quel est le meilleur choix pour implémenter une ligne de jeu ? • La gestion d’une ligne de jeu nécessite de nombreuses méthodes car il faut, par exemple : • contrôler que les ajouts de dominos ne puissent être effectués qu’en début ou fin de jeu • contrôler que les dominos joués soient vraiment accolables à une extrémité de ligne de jeu • permettre de tester si des dominos donnés peuvent être joués ou non La méthode ① (directe) n’est évidemment pas adaptée ici car elle ne permet pas de réaliser un objet qui prenne en charge toutes les fonctionnalités importantes que devrait posséder une ligne de jeu. Méthode ② (indirecte) classe JeuDominos On envisage une nouvelle classe LigneDeJeu // Var. instance privées -ldj:LigneDeJeu ① Encapsulation de l’ArrayList<E> dans la nouvelle classe LigneDeJeu classe LigneDeJeu // Var. instance privées -ldj:ArrayList<Domino> ② Plus proche du paradigme O-O (la ligne de jeu garantie que le jeu est correct) ➽ Le meilleur emplacement est toujours celui qui permet de garantir que des fonctionnalités importantes soient prises en charge par une classe spécifique (=> délégation) Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde
(B) CLASSES GÉNÉRIQUES À SPÉCIALISER:ArrayList<E> ➽ Résumé d’un choix possible entre les 2 méthodes d’utilisation des classes ensemblistes Encapsulation directe ① Encapsulation dans une classe ad hoc ② classe Joueur classe JeuDominos // Var. instance privées -donne:ArrayList<Domino> -nom:String -score:int // Var. instance privées -joueurs:ArrayList<Joueur> -joueurQuiDoitJouer:int -jeuEnCours:boolean -pioche:Pioche -ldj:LigneDeJeu ① ① L’écriture de quelques méthodes est présentée en cours ② ➽ Ici, on n’a pas jugé utile de créer une classe Joueursc’est donc le jeu de dominos lui-même qui devra gérer les joueurs et leurs tour de jouer (on a ajouter 2 variables pour cela) classe Pioche // Var. instance privées -pioche:ArrayList<Domino> ② ① classe LigneDeJeu ➽De plus, on a implicitement dans l’idée qu’un objet JeuDominos fasse jouer un joueur en lui fournissant la pioche et la ligne de jeu... // Var. instance privées -ldj:ArrayList<Domino> ...si ces 2 derniers objets n’existaient pas (au travers de classes dédiées), il serait difficile de demander à un joueur de jouer car comment pourrait-il piocher ou poser son domino sur la ligne de jeu ? ① Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde
ArrayList<E> (exemple de code) Algorithme général : ① faire attention aux conditions de jeu(jeuEnCours) classe JeuDominos // l’ensemble des joueurs et variables de conditions de jeu -joueurs:ArrayList<Joueur> -joueurQuiDoitJouer:int-pioche:Pioche // la pioche -jeuEnCours:boolean-ldj:LigneDeJeu // la ligne de jeu ② déléguer le fait de jouer aux joueurs(joueur.jouerUnCoup(...) boolean hasPlayed = false; // le coup est supposé non joué // ne pas essayer de jouer si le jeu n’a pas commencé ou est terminé if ( this.jeuEnCours ) { } return hasPlayed; // renvoyer un indicateur de coup joué ① // Méthode permettant de faire jouer un coup au bon joueur public boolean jouerUnCoup() { } ③ actualiser les nou-velles conditions de jeu(joueurQuiDoitJouer et jeuEnCours) // faire jouer le joueur qui doit jouer... hasPlayed = // ...et récupérer ce qui s’est passé this.joueurs.get(this.joueurQuiDoitJouer) .jouerUnCoup(this.pioche, this.ligneDeJeu); ② // actualiser l’indice du prochain joueur qui doit jouer... if ( hasPlayed ) { // ...seulement si le joueur a joué this.joueurQuiDoitJouer = (this.joueurQuiDoitJouer+1 == this.joueurs.size() ? 0 : this.joueurQuiDoitJouer+1; } ③ On a utilisé 2 méthodes d’objet ensembliste ③’ // actualiser une éventuelle fin de jeu actualiserFinDeJeu(); // méthode qui mettra à jour this.jeuEnCours Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde
ArrayList<E> (exemple de code) Algorithme général : ① faire attention aux conditions de jeu(pioche ou ligne de jeu incorrecte, donne initiale vide) classe Joueur // ensemble des dominos du joueur -nom:String // le nom du joueur -donne:ArrayList<Domino>-score:int // le score du joueur boolean hasPlayed = false; // le coup est supposé non joué // ne pas essayer de jouer quand c’est impossible if ( ldj!=null && pioche!=null && !this.donne.isEmpty() ) { } return hasPlayed; // renvoyer un indicateur de coup joué // Méthode faisant jouer un coup à ce joueur public boolean jouerUnCoup(Pioche pioche, LigneDeJeu ldj) { } ② essayer de jouer en boucle selon une certaine stratégie ① // essayer de jouer les dominos les uns après les autres... int i = -1; while ( ++i < this.donne.size() ) { } ② ②’ ...en testant des dominos sur la ligne de jeu (ldj.isAccolable(d)) //...en testant si le domino peut être joué sur la ligne de jeu if ( ldj.isAccolable( this.donne.get(i) ) ) { ... } ②’ ②" ...en piochant si nécessaire(pioche.piocher()) //...en piochant si nécessaire (si dernier domino non jouable) else if ( (i+1)==this.donne.size() && !pioche.isEmpty() ) { this.donne.add( pioche.piocher() ); } ②" On a utilisé 8 méthodes d’objet ensembliste (dont 3 qui restent encore à écrire) Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde
ArrayList<E> (exemple de code) ① fournir une méthode permettant de savoir s’il reste des dominos dans la pioche classe Pioche // Méthode indiquant si la pioche est vide public boolean isEmpty() { // utiliser la méthode de l’objet ensembliste encapsulé return this.pioche.isEmpty(); } ① // ensemble des dominos de la pioche -pioche:ArrayList<Domino> ② fournir une méthode permettant de piocher un domino : ② // Méthode permettant de piocher un domino public Domino piocher() { } Algorithme général : ②’ retourner null si la pioche est vide ②’ Domino dominoPioché = null; // aucun domino pioché par défaut // ne pas essayer de piocher si la pioche est vide if ( !isEmpty() ) { } return dominoPioché; // renvoyer le domino pioché (ou null) ②" piocher un domino aléatoirement (Math.random()) ②" // former un indice aléatoire dans [0,taille de la pioche-1] int i = (int)(Math.random()*this.pioche.size()); // extraire le domino de la pioche avant de le retourner dominoPioché = this.pioche.remove(i); On a utilisé 4 méthodes d’objet ensembliste Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde La méthode de classe Math.random() renvoie une valeur double semi-aléatoire dans [0.0 , 1.0[
ArrayList<E> (exemple de code) ① fournir une méthode permettant de savoir s’il y a déjà un domino dans la ligne de jeu classe LigneDeJeu // Méthode indiquant si la ligne de jeu est vide public boolean isEmpty() { // utiliser la méthode de l’objet ensembliste encapsulé return this.ldj.isEmpty(); } ① // ensemble des dominos de la ligne de jeu -ldj:ArrayList<Domino> ② fournir une méthode permettant de savoir si un domino peut être joué : ② // Méthode permettant de savoir si un domino fourni est accolable public boolean isAccolable(Domino d) { } Algorithme général : ②’ boolean accolableOK = (d != null); // vrai si la ligne de jeu est vide et que le domino fourni est valide, faux sinon if ( d!=null && !isEmpty() ) { } // renvoyer la valeur booléenne return accolableOK; ②’ retourner true si la ligne de jeu est vide et que le domino est valide ②" // ici, il faut tester si le domino // est accolable à droite ou à gauche accolableOK = accolableADroite(d) || accolableAGauche(d); ②" sinon, indiquer s’il est accolable à droite ou à gauche On a utilisé 2 méthodes d’objet ensembliste Ces deux méthodes restent à écrire ainsi que de nombreuses autres dans cette classe comme cela a été présenté dans le cas d’encapsulation de tableaux d’objets Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde
Acta est fabula Cours JAVA (I21) -Licence 1 Semestre 2 / Y.Laborde