I21 Algorithmique et programmation II David Gross-Amblard, Joël Savelli IEM-uB GFDL 1.2 CM3 v2.1
i IEM / uB GFDL Encapsulation Déclaration Instanciation Constructions Accès aux membres et droits d'accès Destruction d'instances Accesseurs : un peu de modélisation Membres de classe
i IEM / uB GFDL Accesseurs : motivation ● Classe : modélisation (en Java) d'un concept ● ex. : concept d'étudiant, classe Etudiant ● Questions à se poser : – a) Quelle information décrit complètement une instance (ex. un étudiant donné?) – b) Quelles opérations ou propriétés sont nécessaires/utiles pour ces instances ? – c) Protéger l'intégrité de l'objet : tout privé, comment accéder ?
i IEM / uB GFDL Accesseurs : attribut nom privé ● En lecture public String getNom(){ return nom; } ● En écriture public void setNom(String n){ if (n!=null) nom=n; } ● Comment choisir ?
i IEM / uB GFDL Quelle information décrit complètement une instance ? 1.Identifier les informations définissant une instance 2.Sélectionner uniquement les informations indépendantes 3.Choisir les types concrets (Java) correspondants 4.Définir les attributs de la classe 5.Identifier les valeurs par défaut des attributs 6.Identifier les domaines de validité de chaque attribut 7.En déduire les droits d'accès 8.En déduire les accesseurs et constructeurs
i IEM / uB GFDL Exemple : notion d'étudiant 1.Identifier les informations définissant une instance ● Dépend de l'application ● Pour notre application: nom, prénom, âge, date de naissance, note i21, note i22, moyenne en informatique, nombre de jeton pour le café
i IEM / uB GFDL Exemple : notion d'étudiant 2.Sélectionner uniquement les informations indépendantes ● Âge et date de naissance redondants ● Choix : garder date de naissance (jour,mois,année) ● Choix : âge : sera un calcul (une méthode) ● Moyenne en informatique : entièrement fixé par note i21 et i22 ● Choix : sera un calcul (une méthode)
i IEM / uB GFDL Exemple : notion d'étudiant 3.Choisir les types correspondants ● Nom : String ● Prenom : String ● Jour de naissance : int (short ?) ● Mois de naissance : int (short ?) ● Année de naissance : int (bug de l'an 2000 ?) ● Note i21 : float (double ?) ● Note i22 : float (double ?) ● Jeton café : int (short ?)
i IEM / uB GFDL Exemple : notion d'étudiant 4.Définir les attributs de la classe String nom, prenom int jourNaissance, moisNaissance,anneeNaissance, nbJeton float notei21, notei22 ● (Bonne pratique : convention de nommage)
i IEM / uB GFDL Exemple : notion d'étudiant 5.Identifier les valeurs par défaut des attributs ● NbJeton : par défaut 0 (miracle, comme int) ● Nom, prenom : choix, jamais inconnu ! ● jourNaissance, moisNaissance,anneeNaissance : idem ● notei21, notei22 : inconnues au début ● 0 par défaut ? Confusion avec la note 0 ● Valeur spéciale -1, signifiant inconnu ? ● Indicateur « a ses notes », faux par défaut ? ● Choix : on a les deux notes, ou aucune
i IEM / uB GFDL Exemple : notion d'étudiant 5.Identifier les valeurs par défaut des attributs String nom, prenom int jourNaissance, moisNaissance,anneeNaissance; int nbJeton; float notei21, notei22; Boolean aSesNotes; Dans la suite : le constructeur garantira ces valeurs par défaut
i IEM / uB GFDL Exemple : notion d'étudiant 6.Identifier les domaines de validité de chaque attribut ● Peut-être très difficile ● nom, prénom : jamais null, jamais chaîne vide, pas de chiffre ● jourNaissance, moisNaissance, anneeNaissance : ● Jour entre 1 et 31, mois entre 1 et 12 ● Année supérieure à 1920 ● Contraintes jour/mois ● Contraintes subtiles (ex ?)
i IEM / uB GFDL Exemple : notion d'étudiant 6.Identifier les domaines de validité de chaque attribut ● nbJeton : tous les entiers positifs ou nuls ● notei21, notei22 : entre 0 et 20 ● aSesNotes : vrai ou faux ● Remarque : 20 est une limite un peu arbitraire, en faire une constante
i IEM / uB GFDL Exemple : notion d'étudiant 7.En déduire les droits d'accès ● Si un attribut peut prendre une valeur sortant du domaine du validité, il doit être privé (private) Etudiant e=new Etudiant(); e.notei21= - 50; // typage correct, mais incohérence (ne correspond pas à notre notion d'étudiant)
i IEM / uB GFDL Exemple : notion d'étudiant 7.En déduire les droits d'accès private String nom, prenom private int jourNaissance, moisNaissance,anneeNaissance; private int nbJeton; private float notei21, notei22; private boolean aSesNotes; public final int maxNote=20;
i IEM / uB GFDL Exemple : notion d'étudiant 8.En déduire les accesseurs et constructeurs ● Si tout est privé, comment avoir le nom de l'étudiant ? ● Si tout est privé, comment faire évoluer l'étudiant (ses notes, etc.) ● Accesseur : méthode permettant d'accéder (en lecture ou en écriture) aux informations définissant une instance ● La méthode doit garantir le respect des domaines de validité
i IEM / uB GFDL Accesseurs en lecture ● Accesseurs en lecture public String getNom(){ return nom; } public String getPrenom(){return prenom;} ● Convention (Java) : accesseurs en lecture commencent par get ● Méthodes publiques Etudiant e=new Etudiant(); System.out.println(e.nom); // interdit System.out.println(e.getNom()); // ok
i IEM / uB GFDL Accesseurs en lecture ● Convention : accesseur à valeur booléenne, commence par is public boolean isNoté(){return aSesNotes;} – (désolé pour le franglais, convention pro : tout en anglais...)
i IEM / uB GFDL Acesseurs en lecture ● Interdire lecture des notes si pas noté (Etudiant e) If (e.isNoté()) System.out.println(« i21: »+e.getNotei21()); ● Acesseur public int getNotei21(){ If (isNoté()) return notei21; System.out.println(« Grave erreur ! »); System.exit(-1); // interruption de l'exécution}
i IEM / uB GFDL Accesseurs en lecture ● Plus tard : gestion plus fine des erreurs ● Vocabulaire : accesseur en lecture, sélecteur, getter
i IEM / uB GFDL Accesseurs en lecture ● Et aussi : – GetPrenom, getJourNaissance, getMoisNaissance, getAnneeNaissance, getNbJeton, getNotei22 ● C'est pénible ! – Oui – Outils : en écrire une partie automatiquement (NetBeans ?)
i IEM / uB GFDL Accesseurs en écriture ● Convention (Java) : accesseur en écriture commence par set ● nom, prenom : pas à null, pas la chaîne vide public void setNom(String inom){ If (inom==null || inom.equals('''')){ System.out.println(''Gross malheur !''); System.exit(-1); // interruption de l'exécution } nom=inom; } (on pourrait vérifier la présence de chiffres, etc.)
i IEM / uB GFDL Acesseurs en écriture ● notei21, notei22 : ● Indicateur « a ses notes » ● Choix : on a les deux notes, ou aucune public void setNotes(float inotei21, float inotei22){ If (inotei21>=0 && inotei21 <=maxNote && inotei22>=0 && inotei22 <=maxNote) { notei21=inotei21;notei22=inotei22; aSesNotes=true; } else {sopln(« erreur »); System.exit(-1);}
i IEM / uB GFDL Accesseurs en écriture ● Et aussi : – setPrenom, setDateNaissance, setNbJeton ● Vocabulaire : – Accesseur en écriture, modificateur, mutateur, setter – Geek attitude : « enfin, faut qu'tes setters utilisent tes getters !»
i IEM / uB GFDL Constructeurs ● Valeurs par défaut : ● nbJeton : par défaut 0 (miracle, comme int) ● nom, prenom : jamais inconnu ! ● jourNaissance, moisNaissance,anneeNaissance : idem ● notei21, notei22 : inconnues au début
i IEM / uB GFDL Constructeurs public Etudiant(String iNom, String iPrenom, int iJourNaissance, int iMoisNaissance, int iAnneeNaissance){ nom=iNom; prenom=iPrenom; jourNaissance=iJourNaissance; moisNaissance=iMoisNaissance; anneeNaissance=iAnneeNaissance; nbJeton=0; aSesNotes=false; }
i IEM / uB GFDL Perdu ! ● Etudiant e=new Etudiant(null, '''', -5,-12,0); ● Création d'un étudiant incohérent ● Utiliser les accesseurs !
i IEM / uB GFDL Constructeurs public Etudiant(String iNom, String iPrenom, int iJourNaissance, int iMoisNaissance, int iAnneeNaissance){ setNom(iNom); setPrenom(iPrenom); setDateNaissance(iJourNaissance,iMoisNaissance,iAnneeNaiss ance); nbJeton=0; aSesNotes=false; }
i IEM / uB GFDL Constructeurs ● Présence du constructeur précédent : plus de constructeur par défaut Etudiant e=new Etudiant(); // invalide ● Si pas le constructeur précédent : constructeur par défaut, avec valeur par défaut pour les attributs Etudiant e=new Etudiant(); // valide mais e.Nom vaut null par défaut, incohérent !
i IEM / uB GFDL Constructeur par recopie Etudiant e=new Etudiant(« Onyme », « Anne », 12,12,2012); Etudiant e2=new Etudiant(e); // copie de e ● Constructeur : public Etudiant(Etudiant ie){ setNom(ie.getNom()); setPrenom(ie.getPrenom()); setDateNaissance(ie.getJourNaissance(),ie.getMoisNaissance(), ie.getAnneeNaissance()); If (ie.isNoté()) setNote(ie.getNotei21(),ie.getNotei22()); setJeton(ie.getJeton());}
i IEM / uB GFDL En résumé ● Conception d'une classe : reflexion certaine, processus long, demande de faire des choix ● Classe « vite fait » : tous les attributs publics, pas de contrôle – Développement rapide, mais risque d'erreur important à l'exécution, détection tardive ● Classe « blindée » : tous les attributs privés, contrôle de tous les accès – Développement lent, mais instance jamais incohérente, erreur détectée très rapidement
i IEM / uB GFDL Quelles opérations ou propriétés pour ces instances ? ● Déjà identifié un certain nombre : – Constructeurs – Accesseurs ● Autres propriétés utiles : – l'âge – Avoir ses modules d'Informatique (moyenne >=noteMax/2) – Conversion (ex. représentation textuelle) – Relation entre instances (équivalence, ordre,...)
i IEM / uB GFDL Propriété utiles : âge public int getAgeApproximatif(int annéeCourante){ return annéeCourante-getAnneeNaissance(); }
i IEM / uB GFDL Avoir ses modules d'Informatique (moyenne >=noteMax/2) public boolean isOkPourInfo(){ return getNotei21()+getNotei22()>noteMax; }
i IEM / uB GFDL Conversion ● Si on veut transformer l'instance en un autre type ? ● ex. vers String : représentation textuelle ● Rappel : Etudiant e=new Etudiant(...); System.out.println(e); >
i IEM / uB GFDL Conversion : vers String ● Je veux : « prénom nom (peut offrir X cafés) » ● Norme Java : public String toString(){ return getPrenom()+ '' '' + getNom() + '' (peut offrir '' + getNbJeton() + '' cafés) ''; } System.out.println(e); > John Doe (peut offrir 250 cafés) (plus tard : comment imposer ce type de norme)
i IEM / uB GFDL Relation d'équivalence ● Savoir si deux étudiants sont identiques (i.e. un seul et même objet en mémoire) – e1==e2 ● Savoir si deux étudiants sont égaux : définition ? ● Choix : disons, même nom, prénom, date de naissance
i IEM / uB GFDL Relation d'équivalence ● Norme Java : public boolean equals(Etudiant e){ If ( e.getNom().equals(getNom()) && e.getPrenom().equals(getPrenom()) && e.getJourNaissance()==getJourNaissance()&& e.getMoisNaissance()==getMoisNaissance()&& e.getAnneeNaissance()==getAnneeNaissance()) return true; return false; }
i IEM / uB GFDL Relation d'équivalence Etudiant e1=... Etudiant e2=... e1.equals(e2) // test d'égalité ● (A le bon goût d'être réflexif et transitif)
i IEM / uB GFDL Relation d'ordre total ● Ordonner les étudiants (par beauté, par note, par âge,...?) ● Disons par moyenne (très prof...) public int compare(Etudiant e){ If (e.getMoyenne()<getMoyenne()) return -1; // e est plus petit If (e.getMoyenne()==getMoyenne()) return 0; // e est égal return 1; // e est plus grand }
i IEM / uB GFDL Relation d'ordre total ● Méthode ayant le bon goût d'être : – Reflexive – Transitive – Antisymétrique – Totale : soit e1 =e2
i IEM / uB GFDL Conclusion ● Concevoir une classe demande du temps ● Compromis : niveau de contrôle, temps de développement ● Ariane : grand niveau de contrôle, beaucoup d'argent/temps/développeurs ● Votre projet : – Faire au mieux – On demandera d'insister sur une notion précise
i IEM / uB GFDL Remarque ● Bonne conception : possibilité de remettre en cause les choix initiaux sans tout changer ● Ex : les notes devraient être dans tableau class Etudiant { private float[] notes;... public float getNotei21(){return notes[0];} }
i IEM / uB GFDL Remarque ● Tous le code externe à la classe est inchangé ● Il n'utilise que les accesseurs ! ● Etudiant e=...; ● System.out.println(e.getNotei21()); ● Changement de représentation concrète invisible pour l'utilisateur de la classe (utilisateur : un programmeur utilisant votre classe)