IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Java Discussion :

getter, setter et références sur objets


Sujet :

Langage Java

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre Expert Avatar de rtg57
    Homme Profil pro
    Autodidacte
    Inscrit en
    Mars 2006
    Messages
    1 341
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Autodidacte
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2006
    Messages : 1 341
    Par défaut getter, setter et références sur objets
    Bonjour,

    lorsque j'ai débuté en JAVA, j'avais lu un article concernant la protection des références sur les objets, je crois que cela parlait d'encapsulation.
    Sauf que à l'époque, je n'y comprenais pas grand-chose et j'avais d'autres soucis avec JAVA.

    Actuellement, je développe une application, dont une classe à des objets et variables membres privés.
    Je permets aux autres classes de l'application d'accéder aux valeurs de ces membres, via une interface équipée de getters et setters. Du genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    public void setNomPrenom( String sNomPrenom )
    {
      this.nomPrenom = sNomPrenom;
    }
     
    public String getNomPrenom()
    {
      return this.nomPrenom;
    }
    Comme je commence à comprendre certaines choses en JAVA (enfin c'est ce que je crois ), j'ai l'impression que ce code n'est pas très sécurisé.
    En effet, j'ai lu qu'en JAVA, on transmet des références sur les objets.

    1) Dans setNomPrenom( String sNomPrenom ), je transmets une référence sur un objet String, et je copie cette référence dans l'objet membre nomPrenom.
    Je pense que dans la classe appelante, celle-là même qui a fourni sNomPrenom, je modifie le contenu de cette chaine de caractères, alors je modifie aussi le contenu de nomPrenom ? Vrai ou Faux ??

    2) Dans getNomPrenom(), je transmets une référence sur l'objet membre ( qui est défini comme private), à la classe qui en fait la demande. Celle-ci ayant cette référence, a donc tout loisir de modifier le contenu d'un élément auquel elle ne devrait pourtant pas avoir accès. Vrai ou Faux ??

    Je ne retrouve plus l'article traitant de ce sujet, mais je crois qu'aujourd'hui j'en aurais bien besoin

    Avez-vous des tuyaux à me fournir sur le sujet ?

    Merci & @ bientôt...

  2. #2
    Membre émérite
    Profil pro
    Inscrit en
    Avril 2007
    Messages
    764
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2007
    Messages : 764
    Par défaut
    Alors, déjà, ton exemple est mal choisi...
    En effet, les String sont immutables : les méthodes de la classe String ne modifient jamais l'instance à laquelle elles sont appliquées (les méthodes replace ou substring, par exemple, renvoient une nouvelle instance de String).

    Par contre, sur le principe, tu as raison.
    Si tu donnes directement accès à un objet, tu pourras y appliquer toutes les méthodes de cet objet en question, et donc éventuellement modifier l'état de l'instance concernée.

    Exemple:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    public class Trucmuche
    {
    	private List<String> list;
     
    	public Trucmuche() {
    		this.list = new ArrayList<String>();
    	}
     
    	public void setList(List<String> list) {
    		this.list = list;
    	}
     
    	public List<String> getList() {
    		return this.list;
    	}
     
    	public void print() {
    		System.out.println("Liste à " + this.list.size() + " éléments:");
    		Iterator<String> it = this.list.iterator();
    		while (it.hasNext()) {
    			System.out.println(it.next());
    		}
    		System.out.println("----------");
    	}
     
    	public static void main(String[] args)
    	{
    		Trucmuche chose = new Trucmuche();
    		chose.print();
     
    		chose.getList().add("bli"); // j'ajoute un élément à la liste...
    		chose.getList().add("bla"); // j'ajoute un élément à la liste...
    		chose.getList().add("blu"); // j'ajoute un élément à la liste...
    		chose.print();
    		chose.getList().remove(1); // j'enlève un élément de la liste...
    		chose.print();
     
    		List<String> newList = new ArrayList<String>(Arrays.asList(new String[] {"hihi"}));
    		chose.setList(newList); // je remplace la liste...
    		chose.print();
    		newList.add("huhu"); // j'ajoute un élément à la liste sans même faire référence à chose...
    		chose.print();
    	}
    }
    En donnant directement accès à l'objet list, je permets de faire n'importe quoi avec cet objet.

    La solution pour limiter les actions possibles : l'encapsulation
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    public class Trucmuche
    {
    	private List<String> list;
     
    	public Trucmuche() {
    		this.list = new ArrayList<String>();
    	}
     
    	// la seule action externe autorisée sur la liste : l'ajout d'éléments
    	public void addToList(String item) {
    		this.list.add(item);
    	}
     
    	public void print() {
    		System.out.println("Liste à " + this.list.size() + " éléments:");
    		Iterator<String> it = this.list.iterator();
    		while (it.hasNext()) {
    			System.out.println(it.next());
    		}
    		System.out.println("----------");
    	}
     
    	public static void main(String[] args)
    	{
    		Trucmuche chose = new Trucmuche();
    		chose.print();
     
    		chose.addToList("bli"); // j'ajoute un élément à la liste...
    		chose.addToList("bla"); // j'ajoute un élément à la liste...
    		chose.addToList("blu"); // j'ajoute un élément à la liste...
    		chose.print();
    	}
    }
    Ici, j'ai encapsulé la fonction "ajout d'un élément à la liste" dans la méthode addToList, et c'est la seule action que je présente à l'extérieur de ma classe. Je contrôle les actions que j'autorise l'extérieur à effectuer sur mon objet list.

  3. #3
    Membre Expert Avatar de rtg57
    Homme Profil pro
    Autodidacte
    Inscrit en
    Mars 2006
    Messages
    1 341
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 58
    Localisation : France, Moselle (Lorraine)

    Informations professionnelles :
    Activité : Autodidacte
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2006
    Messages : 1 341
    Par défaut
    Bonjour,

    merci déjà pour la précision au sujet de la classe String.

    Si j'ai bien compris, lorsqu'une classe demande la valeur d'un objet à travers un getter(), il sera judicieux de fournir une instance provisoire "copie" de l'objet:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public Date getDate()
    {
      return new Date( this.maDateMembre );
    ]
    Est-ce que ce raisonnement est juste ?

    Quant à un setter, il faudrait faire un truc du genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    public void setDate( Date laDatetransmise )
    {
      this.maDateMembre = new ( laDateTransmise );
    }
    Est-ce que ça c'est juste aussi ?

    @ bientôt...

  4. #4
    Membre éprouvé

    Profil pro
    Inscrit en
    Mai 2010
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 36
    Par défaut
    Bonjour rtg57,
    [copie dans getter]
    [copie dans setter]

    Est-ce que ça c'est juste aussi ?
    Tu as parfaitement raison: c'est ce qu'on appelle la copie défensive. Accessoirement, tu peux aussi faire tes copies avec clone() particulièrement quand tu ne connais pas la classe concrète des objets que tu manipules.

    Bien sûr, dans les deux cas (new ou clone), cela implique un surcoût lié à la multiplication d'objets copiés. L'alternative est d'utiliser des objets immutables (comme les string).

    http://gfx.developpez.com/tutoriel/java/immuables/

    - Sylvain

  5. #5
    Modérateur

    Profil pro
    Inscrit en
    Septembre 2004
    Messages
    12 579
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2004
    Messages : 12 579
    Par défaut
    À noter que bien que tout cela soit vrai en théorie, et doive bel et bien être appliqué dans certains cas quand on se rend compte qu'il y a risque...

    Dans le monde réel et la plupart du temps, ben, on s'en cogne. Quand un bout de programme n'est pas censé modifier un objet, on peut généralement s'attendre à ce que le programmeur le sache. C'est pas le genre d'erreur qui va arriver spontanément. Et si, par hasard, les choses, l'interface, la manière de faire ; tout ça font que, en fait, l'erreur ait des chances d'arriver ; alors dans ce cas, oui, il faut savoir faire de la copie défensive.
    N'oubliez pas de consulter les FAQ Java et les cours et tutoriels Java

  6. #6
    Membre éprouvé

    Profil pro
    Inscrit en
    Mai 2010
    Messages
    36
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Mai 2010
    Messages : 36
    Par défaut
    Comme souvent en informatique, il n'y a pas de "bonne" ou de "mauvaise" réponse. Tout dépend du contexte.

    Si tu écris une application, tu peux partir du principe que les objets de cette application ne requièreront sans doute pas de copie défensive: après tout, tu peux dire que c'est toi ou ton équipe qui codez les classes correspondantes et qui les utilisez. Et en résumé, vous savez ce que vous faites.

    A l'inverse, si tu écris une bibliothèque - à fortiori une que tu vas distribuer à l'extérieur - j'aurais tout de même tendance à plaider pour la copie défensive. Peut-être pas pour tes classes internes, mais au moins au niveau de tes objets "interfaces" ou de tes "facades". Le premier argument est d'éviter les bugs liés à une mauvaise utilisation. D'accord, comme le souligne thelvin, il faut parfois le chercher. Mais justement, si tu distribues une bibliothèque et que des développeurs se rendent compte qu'avec ce genre de "hack" ils peuvent modifier l'état interne de tes objets pour contourner une limitation ou adapter le comportement de ta bibliothèque à leurs besoins, ils ne vont pas forcément s'en priver
    Et là, implicitement tu rompt l'encapsulation. A partir de ce moment, tu as un problème de couplage fort avec le code client: en effet, si tu modifies ton code, (genre "dans la version 2, on utilise des Calendar au lieu de Date en interne") tu risques de "casser" le code client qui repose sur le fonctionnement interne de tes classe. Selon ta politique, ça peut être le problème du client ("z'ont pas à utiliser des comportements non documentés") ou au contraire ton problème et celui de ton management ("depuis que tu as fais ce changement, l'appli critique de notre plus gros client plante. Va falloir me corriger ça mon gars...").

    ----

    De façon plus terre à terre. Pour palier (partiellement?) à la réduction (minime?) de performances liées à l'utilisation de la copie défensive, la JVM HotSpot server de Java 7 devrait pouvoir éliminer certaines allocations inutiles (http://java.sun.com/javase/7/docs/te...escapeAnalysis). Ca n'est sans doute pas la panacée, mais ça pourrait limiter les scrupules liés à l'utilisation plus systématique de la copie défensive.

    ----

    Maintenant, je répète ce que j'ai dit en préambule: c'est selon ton contexte. L'important, je crois, c'est juste d'être conscient des implications de ce que tu fais.

    A+,
    - Sylvain

  7. #7
    Expert éminent
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Billets dans le blog
    1
    Par défaut
    Salut,

    Citation Envoyé par thelvin Voir le message
    Dans le monde réel et la plupart du temps, ben, on s'en cogne.
    Heu... pas vraiment !

    En Java la plupart des classes de bases sont immuables (String, les différents Numbers...) et la copie est implicite sur les type primitif... Mais le problème reste présent si on utilise un type muable comme Date.

    Ce n'est pas vraiment un problème de mauvaise utilisation volontaire, mais plutôt de mauvaise utilisation involontaire...

    D'ailleurs le fait que la classe Date ne soit pas immuable est une erreur reconnu. C'est un peu pour cela que la plupart de ses méthodes ont été déprécié et que Java 7 devrait inclure une nouvelle API avec des types immuable.


    Par exemple avec ce code :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    Date date = myObject.getDate(); // On récupère la date de l'objet
    date.setTime( date.getTime() + xxx  );
    // puis on utilise date pour autre chose
    1. On récupère la date de l'objet
    2. On la modifie (par exemple on ajoute un jour)
    3. On utilise la date modifié pour un traitement quelconque, indépendant de notre objet

    Ce code semble des plus anodin, mais on a quand même modifier la date de notre objet, en outrepassant le getter...


    Bref : il faut privilégier les types immuables, ou penser à bien protéger ses getter/setter via une copie défensive...

    a++

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Réponses: 0
    Dernier message: 25/02/2014, 16h01
  2. [POO] Perte de la référence sur mon objet (this) lors d'un évènement
    Par muad'dib dans le forum Général JavaScript
    Réponses: 5
    Dernier message: 20/12/2008, 12h59
  3. référence sur la méthode d'un objet
    Par kirbby dans le forum Langage
    Réponses: 2
    Dernier message: 25/04/2008, 11h17
  4. Réponses: 2
    Dernier message: 13/12/2006, 13h39
  5. [VB6] faire référence à un objet situé sur un autre form
    Par coyott dans le forum VB 6 et antérieur
    Réponses: 1
    Dernier message: 15/05/2006, 15h13

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo