Déréférencement d'un objet

Objet commun à plusieurs classes, mais suppression que dans une des classes

Le problème exposé dans ce sujet a été résolu.

Hello,
je suis actuellement sur un projet en Java en cours, et j'ai une question qui me taraude…
J'ai un objet "partagé" avec plusieurs autres objets, et j'aimerais savoir ce qu'il se passe quand je fais "monObjet.objetCommun = null;"

Exemple (Si, si, je vois bien que ce n'est pas clair. En tout cas, moi j'ai du mal à comprendre…) :

1
2
3
4
5
6
7
ObjetPartagé objPartagé = new ObjetPartagé();

UnObjet obj1 = new UnObjet(objPartagé);
UnObjet obj2 = new UnObjet(objPartagé);

obj1.objPartagé = null;
//Est-ce que obj2.objPartagé est à null ou pas?

Tout élément de réponse est le bienvenue :)

+0 -0

Salut Ksass, et merci pour ta réponse
Je sens que je vais passer pour un newbie, mais je ne connais pas le sens de "scope". Je m'en vais voir ce que cela veut dire…

EDIT :
Cela a l'air de correspondre à des environnements correspondant à la limite de portée des variables. Cela voudrait dire que objPartagé n'est pas à null et obj2.objPartagé non plus? (puisque le déréférencement n'aurais de portée que dans le "scope" de obj1)

+0 -0

Cela a l'air de correspondre à des environnements correspondant à la limite de portée des variables.

SeeoX

Je pense qu'en traduisant le mot "scope", tu auras ta réponse :lol: .

Cela voudrait dire que objPartagé n'est pas à null et obj2.objPartagé non plus? (puisque le déréférencement n'aurais de portée que dans le "scope" de obj1)

SeeoX

Teste ;) .

Alors, c'est bizard, mais ça se comporte comme je veux :)

1
2
3
4
5
6
7
8
9
ObjPartage objPartage = new ObjPartage("Hop");
UnObj obj1 = new UnObj(objPartage);
UnObj obj2 = new UnObj(objPartage);

obj1.objPartage.setStringBidon("Hup");

System.out.println(obj1.objPartage.getStringBidon());//affiche Hup
System.out.println(obj2.objPartage.getStringBidon());//affiche Hup
//Les deux objets sont modifiés
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
ObjPartage objPartage = new ObjPartage("Hop");

UnObj obj1 = new UnObj(objPartage);
UnObj obj2 = new UnObj(objPartage);

obj1.setObjPartage(null);

System.out.println(obj1.objPartage.getStringBidon());//affiche une erreur
System.out.println(obj2.objPartage.getStringBidon());//affiche Hop
//Un seul des deux objets est modifié

On va encore me dire que je suis un râleur mais les setters et les getters c'est le mal. Ou, pour nuancer, les getters sont parfois justifiés quand c'est un vrai service, les setters sont généralement des monstres assoiffés de sang.

Le sang de Déméter d'ailleurs. Alors laissez cette pauvre Déméter faire son travail et respectez sa loi. (C'est marrant comment le même type de remarques tombent en même temps sur des sujets complètement décorrélés et sur des forums différents).

Sinon concernant ton code, le scope de obj1.objPartage, c'est obj1, le scope de obj2.objPartage c'est obj2 et le scope de objPartage c'est tout le code que tu montres.

On va encore me dire que je suis un râleur mais les setters et les getters c'est le mal. Ou, pour nuancer, les getters sont parfois justifiés quand c'est un vrai service, les setters sont généralement des monstres assoiffés de sang.

Je ne comprends pas cette remarque, pourrais-tu expliciter un peu?

La loi de Déméter (que j'ai mis en lien plus haut) nous dit que l'on ne doit dialoguer qu'avec 4 catégories d'objets : soi-même, nos composants, les objets qu'on reçoit en paramètre, les objets que l'on crée.

La conséquence de la loi en question est que l'on ne doit rien supposer à propos de la composition des objets autre que soi-même, à savoir que l'on ne doit pas accéder aux composants d'un autre objet (bémol : voir plus loin). Donc évidemment, de ce point de vue les setters et les getters violent complètement ce principe puisqu'on accède explicitement à des champs définis dans l'objet (les setters étant encore pire parce qu'ils font de la modification intrusive).

L'autre élément gênant dans la notion de getters et de setters est que le DIP (Dependency Inversion Principle) nous dit que l'on doit dépendre des abstractions et pas des implémentations. Si on a des getters et des setters sur les attributs de nos classes, on est précisément dans le cas où l'on dépend de l'implémentation de la classe (on dépend de ses attributs).

Les classes sont des fournisseurs de services, pas de vulgaires supports de stockage avec des noms (type structs publiques en C, pour un fournisseur de services en C, on cachera généralement la struct dans un header et l'on donnera une API pour manipuler l'élément en question). Quel service rend une classe qui se contente de faire des get et des set sur des champs internes ? Aucun, le getter ne fait que nous donner une info qui ne nécessite pas de calcul et le setter peut éventuellement nous permettre d'implémenter un contrôle à l'entrée, qui est un contrôle de pré-condition (assert en C et en C++) et qui doit donc disparaître en release : le setter n'a rien apporté d'autre qu'un contrôle que le contrat du type est respecté(*).

Le bémol sur la loi de Déméter c'est principalement deux notions. D'abord l'idée que certain conteneur ont justement pour but de fournir du stockage comme par exemple les tableaux, les tables de hash, les listes … Mais d'une part, ils n'exposent rien de leur implémentation (on ne peut pas accéder à leurs champs en modification ni briser leur invariant, juste aux objets qu'on leur a confié) et d'autre part leur service est justement de dire "je fournis un accès indexé à des objets" ou "je fournis un accès séquentiel à un ensemble d'éléments", etc ....

L'autre bémol est les types qui sont des données. Par exemple, un vecteur à trois composantes, ben c'est juste un vecteur, il y a rien à faire. Cependant, faire un accès par getter/setter pour leurs champs est complètement idiot : on met x, y et z publics et basta.

(*) Et le contrôle de respect du contrat n'est pas un service que doit fournir la classe. Le contrat est quelque chose que doit respecter l'utilisateur de la classe.

Mouais y'a quand même un énorme bémol à mettre sur ce que tu Kass`Peuk "dans la vraie vie" justement.

Pas le temps de détailler là, mais y'a beaucoup de contextes où on ne peut pas faire autrement et changer de langage pour utiliser un ORM typiquement… Je vois pas trop comment faire.

+1 -0

Et énormément d'ORM ont une vision qui ne respecte pas du tout l'orienté objet (typiquement le SRP, l'ISP et le DIP sont complètement passés à la trappe). Ça rime à quoi d'avoir un objet dont le but est d'extraire des informations avec des getNom, getPrenom, etc … Puis tout le bordel pour sauvegarder … Puis tout le bordel pour contrôler … A rien du tout. On va juste se retrouver avec des objets qui ont dix mille responsabilités et un effroyable bordel en terme modularité et de maintenabilité.

Les bases de données comme qui dirait, elles stockent des données, il faut pas s'attendre à ce que le truc qui était à l'intérieur devienne subitement un fournisseur de services parce qu'il est passé dans une moulinette.

Il faut continuer à penser les objets en terme de services. Sur le plan strictement conceptuel on s'en fout de savoir comment sont stockées les données(*), les couches basses feront leur boulot au moment où ce sera nécessaire.

(*) Avant de s'occuper de savoir comment le faire vite, commençons par le faire bien.

Dans le pro je développe pas en OO, la question est vite réglée.

Quant à la question de : est ce que c'est chez les entreprises qu'on trouve des exemples de programmes bien conçus, j'en vois la réponse tous les jours dans les codes que j'analyse, dans les messages des potes qui sont rentrés dans d'autres boîtes, dans les messages sur les forums qui expliquent que bons, le programme est mal branlé mais qu'ils n'ont pas le choix.

Si les entreprises étaient un exemple de bonnes pratiques de programmation ça se saurait depuis le temps. Alors plutôt que d'encourager les développeurs débutants à faire la même chose, j'essaie de leur donner une autre vision.

Je pense que ta vision est un peu biaisé par le fait que tu ne pratiques cette approche. La théorie c'est souvent bien joli, bien beau mais quand ce n'est pas utilisable dans la pratique, ça ne sert à rien.

Ca m'interresserait énormément de voir le code source d'un logiciel relativement important en terme de code et de business, respectant cette théorie à la lettre. Je pourrai ainsi me faire une meilleure idée. Mais pour l'instant, et par rapport à ce que je connais du monde du travail, ça me parait clairement impossible à tenir.

NB: Je trouve notre échange vraiment intéressant :)

+0 -0

(1) Ca m'interresserait énormément de voir le code source d'un logiciel relativement important en terme de code et de business, respectant cette théorie (2) à la lettre. Je pourrai ainsi me faire une meilleure idée.

max-om-93

(1) Je pense que de ce côté, il faudrait voir avec des personne comme Philippe Dunski (koala01) et Luc Hermitte (lmghs). Ce sont principalement eux qui m'ont fait voir des choses comme Demeter et SOLID. Et ils sont dans le pro depuis bien plus longtemps que moi.

(2) Pour reprendre koala01 justement, la différence entre la théorie et la pratique c'est qu'en théorie il n'y en a pas mais qu'en pratique si :lol: . Plus sérieusement, le problème n'est pas de savoir s'il faut respecter ces principes à la lettre mais dans quels moments il est utile de ne pas les respecter (sauf le SRP et le LSP, ceux là j'y tiens).

Je pense que ta vision est un peu biaisé par le fait que tu ne pratiques cette approche. La théorie c'est souvent bien joli, bien beau mais quand ce n'est pas utilisable dans la pratique, ça ne sert à rien. […] Mais pour l'instant, et par rapport à ce que je connais du monde du travail, ça me parait clairement impossible à tenir.

max-om-93

Parce qu'en pratique, bien plus de budget et de temps est alloué à la maintenance qu'au premier développement. Le but du jeu c'est de balancer le plus rapidement un produit qui marche même s'il crache du sang de temps en temps. La question c'est est ce que en prenant plus de temps sur le premier dev (et en respectant ce genre de principes), on aurait une maintenance moins coûteuse ?

Pour moi la réponse est oui, avec Demeter et les SOLID, les tâches sont définies plus clairement, implémentées de manière isolées, et dialoguent avec un nombre restreint d'entités. Cela veut dire que d'une part, il est plus facile de checker les contrats des éléments (objets/fonction) qu'on utilise (ils ne sont pas trop nombreux) et donc de savoir si la connerie vient de la tâche en question ou d'une autre (et auquel cas on répète le processus sur l'autre). Mais d'autre part, les effets de bord (qui nous foutent dedans) sont restreint à nos voisins qui définissent eux mêmes les effets de bords qu'ils autorisent (ce qui est bien plus difficilement contrôlable si on commence à avoir des machin->truc->bidule->chouette).

Bon évidemment le service financier, lui il s'en fout du temps passé à maintenir, il veut des tunes.

Dans les services de la partie métier de mes codes :

  • Pour des logs, des getters, j'en ai quelques uns.
  • Des setters, je les fuis comme la peste. Et si vraiment il y en a un, j'essaie de lui donner un nom plus pertinent (si un contrôle doit être effectué) (cf. l'article d'Emmanuel Deloget), et s'il ne doit y avoir aucun contrôle pour enforcer l'invariant d'une classe, je ne fais pas semblant que la classe rend un service et je laisse le bouzin en public. Et non, mettre derrière setter/getter ce n'est pas respecter l'encapsulation. C'est tout le contraire, c'est une décapsulation. Au mieux, on aide à enforcer des invariants, mais alors là, on ne fait plus du "to set", mais du "to set on condition".

Il y a plein de littérature, et ce même chez les développeurs Java qui ne se sont pas lassés aveuglés par des frameworks et autres ORM qui imposent des setters/getter pour tout.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte