En Java en particulier, mais probablement dans d'autres langages également: quand serait-il utile d'avoir deux références au même objet?
Exemple:
Dog a = new Dog();
Dob b = a;
Y a-t-il une situation où cela serait utile? Pourquoi serait-ce une solution préférée à utiliser a
chaque fois que vous souhaitez interagir avec l'objet représenté par a
?
object-oriented
object-oriented-design
object
Bassinateur
la source
la source
a
etb
toujours référence à la même choseDog
, cela ne sert à rien. S'ils le font parfois , c'est très utile.Réponses:
Un exemple est lorsque vous souhaitez avoir le même objet dans deux listes distinctes :
Une autre utilisation est lorsque vous avez le même objet jouant plusieurs rôles :
la source
Persona batman = new Persona("Batman"); Persona bruce = new Persona("Bruce Wayne"); Persona currentPersona = batman;
- où vous avez plusieurs valeurs possibles (ou une liste de valeurs disponibles) et une référence à celle actuellement active / sélectionnée.currentPersona
pointe vers un objet ou l'autre, mais jamais les deux. En particulier, il peut facilement être possible quecurrentPersona
est jamais réglé surbruce
, auquel cas il est certainement pas une référence à deux objets. Je dirais que, dans mon exemple, les deuxbatman
etcurrentPersona
sont des références à la même instance, mais servent une signification sémantique différente dans le programme.C'est en fait une question étonnamment profonde! L'expérience du C ++ moderne (et des langages issus du C ++ moderne, comme Rust) suggère que très souvent, vous ne voulez pas ça! Pour la plupart des données, vous voulez une référence unique ou unique ("propriétaire"). Cette idée est également la raison principale des systèmes de type linéaire .
Cependant, même dans ce cas, vous voulez généralement des références "empruntées" de courte durée qui sont utilisées pour accéder brièvement à la mémoire, mais qui ne durent pas pendant une fraction significative du temps où les données existent. Le plus souvent, lorsque vous passez un objet comme argument à une fonction différente (les paramètres sont aussi des variables!):
Un cas moins courant lorsque vous utilisez l'un des deux objets en fonction d'une condition, faisant essentiellement la même chose quel que soit votre choix:
Pour revenir à des utilisations plus courantes, mais en laissant les variables locales derrière, nous nous tournons vers les champs: par exemple, les widgets dans les frameworks GUI ont souvent des widgets parents, donc votre grand cadre contenant dix boutons aurait au moins dix références pointant dessus (plus quelques autres de son parent et peut-être des auditeurs d'événements, etc.). Tout type de graphe d'objets, et certains types d'arbres d'objets (ceux avec des références parent / frère), ont plusieurs objets se référant chacun au même objet. Et pratiquement chaque ensemble de données est en fait un graphique ;-)
la source
Variables temporaires: considérez le pseudocode suivant.
Les variables
max
etcandidate
peuvent pointer vers le même objet, mais l'affectation des variables change en utilisant des règles différentes et à des moments différents.la source
Pour compléter les autres réponses, vous pouvez également vouloir parcourir une structure de données différemment, en partant du même endroit. Par exemple, si vous aviez un
BinaryTree a = new BinaryTree(...); BinaryTree b = a
, vous pourriez parcourir le chemin le plus à gauche de l'arbre aveca
et son chemin le plus à droite avecb
, en utilisant quelque chose comme:Cela fait un moment que je n'ai pas écrit Java, de sorte que le code peut ne pas être correct ou sensé. Prenez-le plus comme pseudocode.
la source
Cette méthode est idéale lorsque vous avez plusieurs objets qui rappellent tous un autre objet qui peut être utilisé de manière non contextuelle.
Par exemple, si vous avez une interface à onglets, vous pouvez avoir Tab1, Tab2 et Tab3. Vous voudrez peut-être également pouvoir utiliser une variable commune quel que soit l'onglet sur lequel l'utilisateur se trouve pour simplifier votre code et éviter d'avoir à comprendre à la volée et sur quel onglet votre utilisateur se trouve.
Ensuite, dans chacun des onglets numérotés onClick, vous pouvez modifier CurrentTab pour référencer cet onglet.
Maintenant, dans votre code, vous pouvez appeler "CurrentTab" en toute impunité sans avoir besoin de savoir sur quel onglet vous vous trouvez. Vous pouvez également mettre à jour les propriétés de CurrentTab et elles descendent automatiquement vers l'onglet référencé.
la source
Il existe de nombreux scénarios qui
b
doivent être une référence à un "a
" inconnu pour être utiles. En particulier:b
pointe au moment de la compilation.Par exemple:
Paramètres
t
est une référence à une variable d'une portée extérieure.Valeurs de retour et autres valeurs conditionnelles
biggerThing
etz
sont chacun des références àa
oub
. Nous ne savons pas lequel au moment de la compilation.Lambdas et leurs valeurs de retour
x
est un paramètre (exemple 1 ci-dessus), ett
est une valeur de retour (exemple 2 ci-dessus)Les collections
index["some name"]
est, dans une large mesure, une apparence plus sophistiquéeb
. C'est un alias pour un objet qui a été créé et inséré dans la collection.Boucles
t
est une référence à un élément retourné (exemple 2) par un itérateur (exemple précédent).la source
C'est un point crucial, mais à mon humble avis, cela vaut la peine d'être compris.
Toutes les langues OO font toujours des copies de références, et ne copient jamais un objet «de manière invisible». Il serait beaucoup plus difficile d'écrire des programmes si les langues OO fonctionnaient d'une autre manière. Par exemple, les fonctions et les méthodes n'ont jamais pu mettre à jour un objet. Java et la plupart des langages OO seraient presque impossibles à utiliser sans une complexité supplémentaire importante.
Un objet dans un programme est censé avoir une signification. Par exemple, il représente quelque chose de spécifique dans le monde physique réel. Il est généralement logique d'avoir de nombreuses références à la même chose. Par exemple, mon adresse personnelle peut être communiquée à de nombreuses personnes et organisations, et cette adresse fait toujours référence au même emplacement physique. Donc, le premier point est que les objets représentent souvent quelque chose de spécifique, réel ou concret; et donc être capable d'avoir de nombreuses références à la même chose est extrêmement utile. Sinon, il serait plus difficile d'écrire des programmes.
Chaque fois que tu passes
a
comme argument / paramètre à une autre fonction, par exemple en appelantfoo(Dog aDoggy);
ou en appliquant une méthode à
a
, le code de programme sous-jacent fait une copie de la référence, pour produire une deuxième référence au même objet.De plus, si le code avec une référence copiée se trouve dans un thread différent, les deux peuvent être utilisés simultanément pour accéder au même objet.
Ainsi, dans la plupart des programmes utiles, il y aura plusieurs références au même objet, car c'est la sémantique de la plupart des langages de programmation OO.
Maintenant, si nous y réfléchissons, car le passage par référence est le seul mécanisme disponible dans de nombreux langages OO (C ++ prend en charge les deux), nous pourrions nous attendre à ce qu'il soit le `` bon '' comportement par défaut .
À mon humble avis, l'utilisation de références est la bonne valeur par défaut , pour deux raisons:
Il y a aussi un argument d'efficacité; la copie d'objets entiers est moins efficace que la copie d'une référence. Cependant, je pense que cela manque le point. Les références multiples au même objet ont plus de sens et sont plus faciles à utiliser, car elles correspondent à la sémantique du monde physique réel.
Donc, à mon humble avis, il est généralement logique d'avoir plusieurs références au même objet. Dans les cas inhabituels où cela n'a pas de sens dans le contexte d'un algorithme, la plupart des langues offrent la possibilité de faire un «clone» ou une copie complète. Cependant, ce n'est pas la valeur par défaut.
Je pense que les gens qui soutiennent que cela ne devrait pas être la valeur par défaut utilisent un langage qui ne fournit pas de collecte automatique des ordures. Par exemple, le C ++ à l'ancienne. Le problème est qu'ils doivent trouver un moyen de collecter des objets «morts» et non de récupérer des objets qui pourraient encore être nécessaires; avoir plusieurs références au même objet rend cela difficile.
Je pense que si C ++ avait une collecte des ordures suffisamment peu coûteuse, de sorte que tous les objets référencés soient récupérés, alors une grande partie de l'objection disparaît. Il y aura toujours des cas où la sémantique de référence n'est pas ce qui est nécessaire. Cependant, d'après mon expérience, les personnes qui peuvent identifier ces situations sont également généralement capables de choisir la sémantique appropriée de toute façon.
Je crois qu'il existe des preuves qu'une grande quantité de code dans un programme C ++ est là pour gérer ou atténuer la collecte des ordures. Cependant, l'écriture et le maintien de ce type de code «infrastructurel» augmentent les coûts; il est là pour rendre le langage plus facile à utiliser ou plus robuste. Ainsi, par exemple, le langage Go est conçu en mettant l'accent sur la correction de certaines des faiblesses de C ++, et il n'a pas d'autre choix que la récupération de place.
Ceci n'est bien sûr pas pertinent dans le contexte de Java. Il a également été conçu pour être facile à utiliser, tout comme la collecte des ordures. Par conséquent, avoir plusieurs références est la sémantique par défaut et est relativement sûr dans le sens où les objets ne sont pas récupérés tant qu'il y a une référence à eux. Bien sûr, ils peuvent être conservés par une structure de données car le programme ne se range pas correctement lorsqu'il a vraiment fini avec un objet.
Donc, en revenant à votre question (avec un peu de généralisation), quand voudriez-vous plus d'une référence au même objet? À peu près dans toutes les situations auxquelles je peux penser. Ils sont la sémantique par défaut du mécanisme de passage de paramètres de la plupart des langues. Je suggère que c'est parce que la sémantique par défaut de la manipulation des objets qui existe dans le monde réel doit à peu près être par référence ('car les objets réels sont là-bas).
Toute autre sémantique serait plus difficile à gérer.
Je suggère que le "rover" dans
dl
soit celui effectué parsetOwner
ou les programmes deviennent difficiles à écrire, à comprendre, à déboguer ou à modifier. Je pense que la plupart des programmeurs seraient perplexes ou consternés autrement.plus tard, le chien est vendu:
Ce type de traitement est courant et normal. La sémantique de référence est donc la valeur par défaut, car elle est généralement plus logique.
Résumé: Il est toujours judicieux d'avoir plusieurs références au même objet.
la source
Bien sûr, un autre scénario où vous pourriez vous retrouver avec:
c'est lorsque vous maintenez le code et que vous étiez
b
autrefois un chien ou une classe différente, mais qu'il est désormais desservi para
.En règle générale, à moyen terme, vous devez retravailler tout le code pour vous y référer
a
directement, mais cela peut ne pas se produire immédiatement.la source
Vous voudriez cela à chaque fois que votre programme a une chance de tirer une entité en mémoire à plusieurs endroits, probablement parce que différents composants l'utilisent.
Les cartes d'identité ont fourni un magasin local définitif d'entités afin que nous puissions éviter d'avoir deux ou plusieurs représentations distinctes. Lorsque nous représentons le même objet deux fois, notre client court le risque de provoquer un problème de concurrence si une référence de l'objet persiste dans ses changements d'état avant l'autre instance. L'idée est que nous voulons nous assurer que notre client traite toujours la référence définitive à notre entité / objet.
la source
Je l'ai utilisé lors de l'écriture d'un solveur Sudoku. Lorsque je connais le numéro d'une cellule lors du traitement des lignes, je veux que la colonne contenant connaisse également le numéro de cette cellule lors du traitement des colonnes. Les colonnes et les lignes sont donc des tableaux d'objets Cell qui se chevauchent. Exactement comme l'a montré la réponse acceptée.
la source
Dans les applications Web, les mappeurs relationnels d'objets peuvent utiliser le chargement différé de sorte que toutes les références au même objet de base de données (au moins dans le même thread) pointent vers la même chose.
Par exemple, si vous aviez deux tables:
Chiens:
Propriétaires:
Votre ORM peut effectuer plusieurs approches si les appels suivants sont effectués:
Dans la stratégie naïve, tous les appels aux modèles et aux références associées récupèrent des objets séparés.
Dog.find()
,Owner.find()
,owner.dogs
Etdog.owner
résultat dans une base de données a frappé la première fois, après quoi ils sont enregistrés dans la mémoire. Et donc:Sans référence, vous avez plus de récupérations, utilisez plus de mémoire et introduisez la possibilité d'écraser les mises à jour antérieures.
Supposons que votre ORM sache que toutes les références à la ligne 1 du tableau des chiens doivent pointer vers la même chose. Alors:
En d'autres termes, l'utilisation de références aide à codifier le principe d'un seul point de vérité (au moins à l'intérieur de ce fil) étant la ligne de la base de données.
la source