ntro.ca

        • Contrats de classe
        • Liens utiles
        • Calendrier
        • Calendrier groupe 2
        • Calendrier groupes 1, 3
        • Structure du cours
        • Évaluations
        • Matériel à se procurer
        • Les profs
          • Marc-Olivier Tremblay
          • Mathieu Bergeron
        • Module 1.1: installation + trier des cartes
        • Module 1.2: rappels POO
        • Module 1.3: tableau d'objets
        • Examen 1
        • Module 2.1: données JSON
        • Module 2.2: données en Java
        • Module 2.3: récursivité
        • Examen 2
        • Module 3.1: structure générique
        • Module 3.2: efficacité (1)
        • Module 3.3: efficacité (2)
        • Examen 3
        • Module 4.1: liste naïve
        • Module 4.2: liste par tableau
        • Module 4.3: liste chaînée
        • Examen 4
        • Module 5.1: mappage naïf
        • Module 5.2: mappage par hachage
        • Module 5.3: mappage par arbre
        • Examen 5
        • Équipes
          • Horaire groupe 1
          • Horaire groupe 2
          • Horaire groupe 3
          • Groupe 1
          • Groupe 2
          • Groupe 3
        • Projets vedettes 2022
        • Projets vedettes 2023
        • Projets vedettes 2024
        • Survol
        • Structure
        • Calendrier
        • Calendrier des séances
        • Évaluations
        • Exemples de jeu
        • Exemples de pages
        • Réponses à vos questions
        • Module 1: créer le projet
        • Module 2: concevoir l'application
        • Module 3: vues NtroFx
        • Module 4: modèle et navigation
        • Module 5: ajouter le dorsal, modifier le modèle
        • Module 7: améliorer l'affichage
        • Module 8: jeu en 2d
        • Module 9: client/serveur
        • Module 10: plusieurs instances du même modèle
        • TP1
        • Examen 1
        • TP2
        • Examen 2
        • Projet de fin de session
      • Ajout Format JSON
        • Calendrier
        • Évaluations
        • Structure du cours
        • Contrat de classe
        • Le prof
        • 01: Windows et Word
          • Astuces et raccourcis
        • 02: Word
        • 03: Word
          • Exercice Word: insertion d'éléments spéciaux
          • Exercice Word: tableaux
        • 04: Word
          • Exercice Word: références
          • TP01: Word (15%)
        • 05: PowerPoint
          • TP02: PowerPoint (10%)
        • 06: Examen Word (20%)
        • 07: Excel
        • 08: Excel
        • 09: Excel
          • TP03: Excel (15%)
        • 10: Excel
        • 11: Examen Excel (20%)
        • 12: Access
        • 13: Access
        • 14: Access
        • 15: Examen Access
      • Sondage H2023 (dept. info)
      • Vision H2023 (dept. info)
      • P1) exercices interactifs de lecture
      • P2) transition Excel vers Python
        • Atelier 2: un exemple
      • Index
      • Point de vue sur l'IA
    Théorie 1.2.3: visibilités et attributs
    • Théorie 1.2.3: visibilités et attributs
      • Pourquoi différentes visibilités?
      • API: Application Programming Interface
      • Les règles clés
      • Quelle visibilité choisir?
      • Les attributs et la visibilité
      • Les attributs représentent l’état privé d’un objet
      • Exemple: accepterChargement et consommationLitresParKilometres
      • Exemple: formater
      • Pour écrire du code lisible: apprendre à déléguer

    Théorie 1.2.3: visibilités et attributs #

    • Une méthode peut être visible (public) ou cachée (private), selon le contexte

    • Si la méthode est cachée pour moi, je ne peux pas l’appeler

    • Une méthode cachée n’apparaîtra pas dans les auto-complétions d’Eclipse

    Pourquoi différentes visibilités? #

    • Prenons l’exemple de l’outil de validation.

    • Le prof ne veut pas que l’étudiant voit tout le code de l’outil de validation

      • par exemple, le prof veut:

      • et non:

      • la méthode validateTestCases ne doit pas être appelée par l’étudiant

      • alors vaut mieux la cacher en la déclarant private

    API: Application Programming Interface #

    • La visibilité est fortement liée à la notion d’API

    • L’API document les méthodes d’une librairie que l’application peut appeler

    • P.ex. l’API Java documente les méthodes utiles à un programmeur Java

    • Il n’a pas de méthodes private dans cette documentation.

      • le programmeur n’en a pas besoin
      • les détails d’implantation sont inutiles 99% du temps

    Les règles clés #

    • Voici les règles de base pour la visibilité
    Visible de l'extérieur de la classe Visible des sous-classes Héritable/redéfinissable
    private NON NON NON
    protected NON OUI OUI
    public OUI OUI OUI
    • Il y a d’autres règles, mais les règles ci-haut couvrent 90% des cas.

    Quelle visibilité choisir? #

    • Il faut se poser les questions:
      • Est-ce que le reste du programme a besoin d’appeler cette méthode?
        • si oui, alors choisir public
      • Est-ce qu’une sous-classe aura besoin de redéfinir cette méthode?
        • si oui, alors choisir protected
      • Sinon, choisir private

    Les attributs et la visibilité #

    • Il y a deux types d’attributs:

      • attribut défini: l’attribut est défini dans la classe
      • attribut hérité: l’attribut est défini dans la classe parent (public ou protected)
    • Il n’y a pas d’attribut redéfini.

      • pour accéder à l’attribut d’une sous-classe, il faut utiliser une méthode

    Les attributs représentent l’état privé d’un objet #

    • En général, tous les attributs d’un objet sont cachés (private)

    • Les attributs représentent l’état de l’objet, p.ex:

      • le kilométrage parcouru par le véhicule
      • le nombre d’oiseaux attrapés par le chat
    • L’état est ce qui peut changer en cours d’exécution

      • (alors que le code des méthodes reste toujours le même)
    • L’état est privé parce que l’objet doit toujours être appeler de la même façon

      • l’état est un détail d’implantation qui ne concerne pas le reste du programme

    Exemple: accepterChargement et consommationLitresParKilometres #

    • Considérer la classe Vehicule:
    public class Vehicule {
    
    	private double totalKilometres = 0;
    	
    	public void rouler(double kilometres) {
    		totalKilometres += kilometres;
    	}
    
    	private double litresEssenceConsomes() {
    		return totalKilometres * consommationLitresParKilometre();
    	}
    }
    
    • Le calcul de consommation d’essence est simple

    • Dans la classe Camion, on ajout une notion de chargement:

    public class Camion extends Vehicule {
    	
    	private double consommationDeBase = 14;
    	private double chargementEnKilos = 0;
    
    	public void accepterChargement(double chargementEnKilos) {
    		this.chargementEnKilos = chargementEnKilos;
    	}
    
    	@Override
    	protected double consommationLitresParKilometre() {
    		return calculerConsommationSelonChargement();
    	}
    
    	private double calculerConsommationSelonChargement() {
    		return (1 + chargementEnKilos / 1E6) * consommationDeBase;
    	}
    }
    
    • La notion de chargement est cachée grace à la visibilité private

    • On ne veut vraiment pas que le Vehicule gère la notion de chargement:

    public class Vehicule {
    
    	private double totalKilometres = 0;
    	
    	public void rouler(double kilometres) {
    		totalKilometres += kilometres;
    	}
    
    	private double litresEssenceConsomes() {
    	    double litresConsomes = 0;
    
    	    if(this instanceof Camion){ // instanceof
    
    	        double chargementEnKilos = ((Camion) this).getChargementEnKilos(); // getter
    
    	        double consommationDeBase = ((Camion) this).getConsommationDeBase(); // getter
    
    		    double consommationSelonChargement = (1 + chargementEnKilos / 1E6) * consommationDeBase;
    
    	        litresConsomes = totalKilometres * consommationSelonChargement;
    
    	    }else{
    
    	        litresConsomes = totalKilometres * consommationLitresParKilometre();
    	    }
    
    		return litresConsomes;
    	}
    }
    
    • Le code serait beaucoup plus compliqué et difficile à lire

    • En plus, on veut que Camion puisse changer son code privé

    public class Camion extends Vehicule {
    	
    	private double consommationDeBase = 14;
    	private double chargementEnTonnes = 0; // TONNES
    
    	public void accepterChargement(double chargementEnKilos) {
    		this.chargementEnTonnes = chargementEnKilos / 1E3;  // TONNES
    	}
    
    	@Override
    	protected double consommationLitresParKilometre() {
    		return calculerConsommationSelonChargement();
    	}
    
    	private double calculerConsommationSelonChargement() {
    		return (1 + chargementEnTonnes / 1E3) * consommationDeBase; // TONNES
    	}
    }
    
    • Camion représente maintenant le poids du chargement en tonnes
    • toutes les modifications sont privées à la classe Camion
    • il n’y a rien à changer ailleurs dans le code

    Exemple: formater #

    • Considérer la méthode pour formater
    public class Vehicule {
    
    	public String formater() {
    		StringBuilder builder = new StringBuilder();
    		
    		if(siNomFeminin()) {
    			builder.append("Ma ");
    		}else {
    			builder.append("Mon ");
    		}
    		builder.append(nomVehicule());
    		builder.append(" a roulé ");
    		builder.append(totalKilometres);
    		builder.append(" kilomètres et consomé ");
    		builder.append(litresEssenceConsomes());
    		builder.append(" litres d'essence.");
    		
    		return builder.toString();
    	}
    }
    
    • La méthode est simple parce qu’on délègue le plus possible

    • Si on ne délègue pas, ça donne plutôt:

    public class Vehicule {
    
    	public String formater() {
    		StringBuilder builder = new StringBuilder();
    
    		if(this instanceof Auto){ // instanceof
    
    			builder.append("Mon");
    
    		}else if(this instanceof Camion){ // instanceof
    
    			builder.append("Mon");
    
    		}else if(this instanceof Moto){ // instanceof
    
    			builder.append("Ma");
    
    		}else if(this instanceof Mobilette){ // instanceof
    
    			builder.append("Ma");
    		}
    
    		if(this instanceof Auto){ // instanceof
    
    			builder.append(" auto");
    
    		}else if(this instanceof Camion){ // instanceof
    
    			builder.append(" camion");
    
    		}else if(this instanceof Moto){ // instanceof
    
    			builder.append(" moto");
    
    		}else if(this instanceof Mobilette){ // instanceof
    
    			builder.append(" mobilette");
    		}
    
    		builder.append(" a roulé ");
    		builder.append(totalDesKilometres());
    		builder.append(" kilomètres et consomé ");
    		builder.append(litresEssenceConsomes());
    		builder.append(" litres d'essence.");
    		
    		return builder.toString();
    	}
    }
    
    • Imaginer qu’on ajoute la sous-classe Fourgonnette

    • Pour la première version, il n’y a rien à modifier!

    • Pour la deuxième version, il faut ajouter du code:

    public class Vehicule {
    
    	public String formater() {
    		StringBuilder builder = new StringBuilder();
    
    		if(this instanceof Auto){ // instanceof
    
    			builder.append("Mon");
    
    		}else if(this instanceof Camion){ // instanceof
    
    			builder.append("Mon");
    
    		}else if(this instanceof Moto){ // instanceof
    
    			builder.append("Ma");
    
    		}else if(this instanceof Mobilette){ // instanceof
    
    			builder.append("Ma");
    
            // FOURGONNETTE
    		}else if(this instanceof Fourgonnette){ // instanceof
    
    			builder.append("Ma");
    		}
    
    		if(this instanceof Auto){ // instanceof
    
    			builder.append(" auto");
    
    		}else if(this instanceof Camion){ // instanceof
    
    			builder.append(" camion");
    
    		}else if(this instanceof Moto){ // instanceof
    
    			builder.append(" moto");
    
    		}else if(this instanceof Mobilette){ // instanceof
    
    			builder.append(" mobilette");
    
            // FOURGONNETTE
    		}else if(this instanceof Fourgonnette){ // instanceof
    
    			builder.append(" fourgonnette");
    		}
    
    		builder.append(" a roulé ");
    		builder.append(totalDesKilometres());
    		builder.append(" kilomètres et consomé ");
    		builder.append(litresEssenceConsomes());
    		builder.append(" litres d'essence.");
    		
    		return builder.toString();
    	}
    }
    

    Pour écrire du code lisible: apprendre à déléguer #

    • j’imagine que ma classe est une librairie pour le reste du programme

      • qu’est-ce que ma classe devrait cacher? Rendre public?
    • j’essaie de déléguer un comportement dès que possible:

      • à une autre méthode
      • à une autre classe
      • à une sous-classe
    • en particulier, je me méfie des cas où:

      • ma méthode a plus de 20 lignes
        • je peux sûrement déléguer une partie à une autre méthode
      • j’utilise beaucoup de instanceof et de transtypage (casting)
        • je peux sûrement déléguer aux sous-classe
      • j’utilise beaucoup d’accesseurs (getter)
        • je peux sûrement déléguer à l’objet que j’appelle
    • NOTES:

      • les règles ci-haut couvrent 90% des cas
      • comme d’habitude, il y a des exceptions
        • il y a des cas où une méthode de 20+ lignes est plus lisible
        • il y a des cas où utiliser un accesseur est plus lisible
        • il y a des cas où utiliser instanceof est plus lisible
    Creative Commons License Creative Commons Attribution Creative Commons ShareAlike
    • Théorie 1.2.3: visibilités et attributs
      • Pourquoi différentes visibilités?
      • API: Application Programming Interface
      • Les règles clés
      • Quelle visibilité choisir?
      • Les attributs et la visibilité
      • Les attributs représentent l’état privé d’un objet
      • Exemple: accepterChargement et consommationLitresParKilometres
      • Exemple: formater
      • Pour écrire du code lisible: apprendre à déléguer