Incomprehension sur des attributs "protected" des classes

Résolu/Fermé
glennog Messages postés 293 Date d'inscription jeudi 3 janvier 2013 Statut Membre Dernière intervention 19 novembre 2013 - 15 mars 2013 à 17:24
glennog Messages postés 293 Date d'inscription jeudi 3 janvier 2013 Statut Membre Dernière intervention 19 novembre 2013 - 19 mars 2013 à 19:56
Bonjour,


je suis en train de lire le tuto de java ( qui est sur le site du zero ).
et je suis sur le chapitre de la programmation Objet orienté ,.
Et j'ai lu que pour empecher l'utiliser de pourvoir modifier directement les variables d'instance ,
on les attribue la portée " private " et on utilisait les accesseurs et les mutateurs ,
mais si des classes héritent de notre première classe , il nous fallait attribuer la portée "protected"

mais je viens de faire une remarque , :
en attribuant la portée protected à nos variables d'instance ,
l'utilisateur peut modifier directement ( sans passer par les mutateurs ) les variables d'instance .
est ce correct ??
en principe , on devrait toujours empecher l'utilisateur de les modifier directement ( sans mutateurs ) non ???

enfin , je pense que même en les declarant " protected" ,
on devrait toujours empecher l'utilisateur d'avoir accès aux differents variables d'instance.
pourriez vous m'éclairer un peu sur ce point ?


3 réponses

KX Messages postés 16733 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 janvier 2024 3 015
15 mars 2013 à 17:55
Lorsque l'on hérite une classe on fait en général de la surcharge, c'est à dire que l'on modifie les méthodes de la classe mère (parfois les get et les set aussi) pour mieux correspondre à la spécificité de la classe fille.
Mais pour faire cela il faut souvent avoir accès aux champs protected, parce que s'ils étaient private on devrait alors reconstruire l'information qui existe et on aurait alors des doublons qui seraient contre-productifs puisqu'il faudrait gérer la même information deux fois : d'une part avec les attributs private de la classe mère avec des get/set, et d'autre part avec les attributs private de la classe fille, et il faudrait s'assurer de la cohérence des données, ce qui serait bien trop lourd.

En fait, il faut bien choisir qu'est-ce qui sera private et qu'est-ce qui sera protected. En gros si il y a un attribut qui est vraiment crucial, et que si on le modifie n'importe comment plus rien ne marche, alors il faudra le mettre private, mais si c'est juste un champ de donnée que l'on peut modifier sans soucis, alors protected ne pose aucun problème.

Il existe un autre moyen de "protéger" un attribut, c'est de le déclarer final, dans ce cas il ne pourra prendre qu'une seule valeur, et ne pourra jamais être modifié, ni en interne, ni en externe. Dans ce cas tu peux même mettre l'attribut public, il n'y aura aucun risque de perturber son fonctionnement (c'est le cas par exemple de System.in, System.out, et System.err, ce sont trois attributs public final static de la classe System)

Exemple :

public class Personne
{
    // champs de données qui peuvent être modifiées sans risque pour le traitement et peuvent donc être partagés par de futurs développeurs
    protected String nom;
    protected String prenom;

    // champs spécifique aux traitements à ne surtout pas modifier
    // permet de donner un identifiant unique à chaque objet de la classe
    private static int globalId = 0;
    private final int internalID = globalID++;
}
1
Salut,

Un attribut public est accessible de partout, et un attribut private n'est accessible que depuis la classe à laquelle il appartient.

Un attribut protected se comporte comme un private, mais il est en plus accessible depuis les classes qui héritent de la classe dans laquelle il est défini.
Par exemple tu as 3 classes A, B et C, avec B implement A. Si tu définit dans A un attribut protected, tu pourra le modifier depuis A et B, mais pas depuis C.

Cela peut être utile par exemple si tu veux surcharger une fonction pour en modifier le comportement dans ta classe fille.
0
glennog Messages postés 293 Date d'inscription jeudi 3 janvier 2013 Statut Membre Dernière intervention 19 novembre 2013 4
15 mars 2013 à 18:05
salut l'ami , je comprends parfaitement ce que tu dis , au fait , je voulais savoir ,
considérons que j'ai une classe appelée homme

public Homme{

// ensuite que j'ai des attributs que je déclare protected afin qu'ils puissent être accessibles pas mes classes qui héritent de la classe mère Homme

protected String nom = "inconnu";
protected int categorie = 2;


et que j'ai ensuite une autre classe qui hérite de ma classe mère Homme et que cette classe s'appelle par exemple personnage ,

j'aurais alors dans ma classe personnage , le code suivant ( c'est juste un exemple )

public Personnage extends Homme {

// traitement à effectuer;
}


maintenant dans mon main , si jamais , je déclare un objet H1

Personnage h1 = new Personnage;


en principe , je ne devrais pourvoir accès directements aux variables d'instance de ma classe Homme ,

mais bizarement quand je crée un nouvel objet H1 par exemple


j'arrive à faire ceci : h1.variabled'instancedeclaréeprotected

et pourtant , je ne devrais pas y avoir accès ainsi directement sans passer par des acceseurs ou des mutateurs non , ou bien , je ne comprends pas , car si j'arrive à faire cela , je ne vois pas ou est l'avantage lorsque je déclare mes variables d'instance protected ( je repète , je sais bien pourquoi , je dois attribuer la portée de mes variables d'instance protected ) ,

mais , je dis que je ne comprends pas pourquoi est ce que j'arrive à y accéder dircetement sans passer par des mutateurs ou encore par des accesseurs
ou bien est ce qu'il y'a une erreur dans mon code , si c'est le cas , pourrasi tu m'indiquer laquelle ?
merci de ton attention
0
KX Messages postés 16733 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 janvier 2024 3 015
15 mars 2013 à 18:22
"pourquoi est ce que j'arrive à y accéder dircetement sans passer par des mutateurs ou encore par des accesseurs"
Où est-ce que tu as fait ton main ? Dans le même fichier que la classe Homme ? Peut-être dans la même classe ? Dans ces deux cas c'est normal que tu puisses accéder à tes variables protected. Mais si tu fais un main dans une classe de Test dans un fichier à part, tu ne devrais plus pouvoir faire comme ça...
0
glennog Messages postés 293 Date d'inscription jeudi 3 janvier 2013 Statut Membre Dernière intervention 19 novembre 2013 4
Modifié par glennog le 16/03/2013 à 12:36
non , j'ai deux classes dans mon package par défaut
mais , j'ai pourtant séparé mon fichier qui contient du main , de mes classes , voici mon code


ici c'est le main , comme tu le vois , je peux passer par mes accesseurs et aussi par des mutateurs pour avoir accès aux champs de mes variables d'instance , mais je peux aussi accéder directements aux champs de ma classe , :


import java.util.Scanner; 
import java.math.*; 



public class sdz1 { 

  
  
 public static int x,y,c; 
  

 public static void main(String[] args) 
 { 
   
  Ville vb = new Ville(); 
   
   
  Scanner sc = new Scanner(System.in); 
  String name ;   
  Capitale capitale1 = new Capitale(); 
  Ville ville1 = new Ville(); 
  Ville nouvelleVille1 = new Ville("Abidjan", 32000, "Côte d'Ivoire"); 
  Ville nouvelleVille2 = new Ville("Yamoussokro", 1500, "Bless City"); 
   
  System.out.println("v=" + ville1.getNom()+" ville de " + ville1.getHabitant()+" habitants se situant en "+ ville1.getPays() ); 
  System.out.println("v1 = " + capitale1.getHabitant()); 
   
  System.out.println("possibilite d'acceder a des variables d'insrabce " + ville1.nbreHabitant); 

  } 
   
    
    
   }    





et maintenant , voici le code de ma classe ville , comme tu peux le constater , j'ai bien défini la portée de mes variables d'instance à protected et pourtant :

voici le contenu de ma classe ville

public class Ville { 
 protected String nomVille; 
 protected String nomPays; 
 protected  int nbreHabitant; 

 public Ville(){ // constructeur par défaut 
  System.out.println("création d'une nouvelle Ville"); 
  nomVille =" Inconnu"; 
  nbreHabitant =0; 
  nomPays="Inconnu";    
 } 
  
 public Ville (String Iville, int Ihabitant, String Ipays) // nouveau constructeur 
 { 
  System.out.println("Création d'une ville avec des paramètres"); 
  nomVille = Iville; 
  nbreHabitant = Ihabitant; 
  nomPays = Ipays;     
 } 
  
 public String getNom(){ 
  return nomVille; 
 } 
  
 public int getHabitant(){ 
  return nbreHabitant; 
 } 
  
 public String getPays(){ 
  return nomPays; 
 } 
  
  
 public void setPays(String Ipays){ 
  nomPays = Ipays; 
   
 } 
  
 public void setnom(String Iville){ 
  nomVille = Iville; 
 } 
  
 public void setHabitant(int Ihabitant){ 
  nbreHabitant = Ihabitant; 
 }   
  
 } 



dis , tu peux m'aider un peu , car j'ai beau me retourner la question , fouiller dans le projet , mais jusque là , je n'arrive toujours pas à savoir pourquoi est ce que j'arrive à accéder directement à mes variables d'instance

merci de ton aide et du temps que tu m'accordes l'ami
0
KX Messages postés 16733 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 janvier 2024 3 015
16 mars 2013 à 13:45
Tu peux accéder aux membres protected de Ville depuis la classe sdz1 parce qu'elles sont dans le même package. Il faut bien comprendre que l'encapsulation protected est plus proche du public que du private, puisque son but est bien de pouvoir être partagée par tous les développeurs qui voudraient accéder à ses fonctionnalités, et enrichir le package de nouvelles classes (par héritage ou non).

Quelques remarques sur ton code :

1. Le constructeur par défaut n'est pas obligatoire, et ici il ne sert à rien car aucune ville ne s'appelle Inconnu d'un pays Inconnu avec 0 habitants... Tu devrais supprimer ce constructeur.
2. Une ville ne change jamais de nom, ou alors dans des cas tellement rares que l'on peux considérer que ce sera une nouvelle ville, tu devrais donc enlever ton setNom, et considérer alors que l'attribut nom est final (de manière général, essaye de mettre final autant que possible). Pour éventuellement prévoir le cas où la ville change de nom, utilise un constructeur qui prend l'ancienne ville, un nouveau nom, et qui donne la nouvelle ville.
3. Pour moi le pays ne devrait pas être un String, mais un objet d'une classe à part, qui serait déterminé entre autre par sa capitale. Ce qui permet de modifier le nom du pays directement dans l'objet pays, sans avoir à modifier toutes les villes. Et si vraiment la ville change de pays alors on ferait cela dans un constructeur pour obtenir un nouvel objet Ville.

Si j'insiste sur ces points de "logique" c'est parce que la programmation objet fait intervenir la sémantique dans la construction des classes. Si tu fais juste de la manipulation d'attributs, et que tous ont getteur et setteurs, alors ce n'est plus vraiment du Java car tu ne fais plus de POO.

Remarque : il réside une difficulté dans ce que j'ai dis, car un Pays est construit en donnant en paramètre sa capitale, et la capitale (qui est une ville) est construite en donnant le pays. On ne peut donc pas résoudre ce problème simplement, il faut tricher un peu et créer la capitale dans le constructeur du Pays (ce qui au final est plutôt logique aussi).

public class Ville
{
    public final String nom;
    public final Pays pays;
    private int population;

    public Ville (String nom, Pays pays, int population)
    {
        this.nom = nom;
        this.pays = pays;
        this.population = population;
    }
    
    public Ville (String nom, Pays pays)
    {
        this(nom,pays,0);
    }
    
    public int population()
    {
        return population;
    }
    
    public void setPopulation(int population)
    {
        this.population = population;
    }
}

public class Capitale extends Ville
{
    public Capitale(String nom, Pays pays, int population)
    {
        super(nom, pays, population);
    }
}

public class Pays
{
    public final String nom;
    public final Capitale capitale;
    
    public Pays(String nomPays, String nomCapitale, int populationCapitale)
    {
        nom = nomPays;
        capitale = new Capitale(nomCapitale,this,populationCapitale);
    }    
}

public class Test
{
    public static void main(String[] args)
    {
        Pays coteIvoire = new Pays("Côte d'Ivoire","Yamoussoukro",242744);
        Capitale yamoussoukro = coteIvoire.capitale;
        Ville abidjan = new Ville("Abidjan",coteIvoire,3802000);        
    }
}
0
glennog Messages postés 293 Date d'inscription jeudi 3 janvier 2013 Statut Membre Dernière intervention 19 novembre 2013 4
18 mars 2013 à 22:00
merci d'avoir repondu à ma préoccupation , et aussi merci pour ton avis , il me sera d'une grande utilité pour m'améliorer , passe une bonne soirée .

mais dis , j'ai encore quelque chose qui me derange un peu , tu as dis que je pouvais avoir accès à mes variables d'instance car mes classes se trouvent dans le même package que mon fichier main.

mais j'aimerais savoir , dis , dans mes recherches , il est dit que l'on ne peut avoir accès aux classes declarée sans la portée public , du genre

si dans une classe , j'ai ceci
 class personnage   
, alors je ne peux pas avoir accés à cette classe en déhors du package , dans lequel ma classe a été créée

mais par contre si j'attribue la portée
 public class personnage 
, alors , je peux avoir accès à ma classe , que je mon main soit dans le même package ou non.

mais j'aimerais savoir , n'y a t'il pas un moyen d'avoir un main dans le même package qu'une super classe , et tout en utilisant la portée protected ??? pour ne pas que l'utilisateur ait accès directement aux variables d'instance ??

pour faire cela , je pense à une interface ou une classe abstraite , dans laquelle ,je declare mes variables d'instance protected ( et comme ma classe abstraite est impossible à instancier) , alors , dans chaque classe qui implemente ma super classe , je declare mes variables d'instance en leur attribuant la portée private.

encore merci pour tout ce temps que tu m'accordes , mon ami , milles merci
0
KX Messages postés 16733 Date d'inscription samedi 31 mai 2008 Statut Modérateur Dernière intervention 31 janvier 2024 3 015
19 mars 2013 à 19:35
Attention ! Il faut bien distinguer trois rôles dans la vie d'une classe :

1) la conception de la classe, pour gérer son fonctionnement interne
2) le développement autour de cette classe, notamment l'héritage au sein du package
3) l'utilisation de la classe, telle quelle.

Tu as tendance à confondre 2 et 3, en parlant "d'utilisateur" là où on devrait parler de développeur.
L'utilisateur travaille forcément dans un autre package que celui où tu as définis tes classes, il n'aura donc accès aux données protected que lorsqu'il fera un héritage de ta classe.
En particulier la méthode main devrait donc être dans un autre package que tes classes.

En revanche le développeur pourra utiliser protected dans le même package et c'est le but, je l'avais déjà expliqué plus haut (ici) :

"l'encapsulation protected (...) son but est bien de pouvoir être partagée par tous les développeurs qui voudraient accéder à ses fonctionnalités, et enrichir le package de nouvelles classes"

Si par exemple moi je décide d'aller coder ma classe dans java.util et d'aller utiliser des méthodes protected de la classe String par exemple, rien ne m'en empêche, mais c'est parce que le concepteur (1) aura autorisé que moi développeur (2) je puisse accéder à ces fonctionnalités. L'utilisateur (3) devra lui se contenter des méthodes public. Mais de toute façon, les fonctionnalités sensibles je n'y aurais pas accès car elles auront été encapsulées private, à moins bien sûr d'aller modifier directement le code source de la classe String (ce qui est également possible... avec ou sans le code source d'ailleurs !).

Bref, dans tous les cas ces encapsulations pourront être contournées si le développeur en a vraiment besoin. Il ne s'agit donc pas d'une protection des données, mais uniquement de donner plus ou moins de visibilité pour donner à l'utilisateur tout ce dont il a besoin et qu'il peux utiliser sans risque, et au développeur ce qu'il peux utiliser à la rigueur pour des besoins spécifiques de développement.

Mais n'oublie pas comme je l'ai déjà dit qu'il y a d'autres moyens de protéger ses attributs, comme le mot-clé final qui permet à une valeur d'être lue sans possibilité d'être modifiée.
0