31 votes

Le "passage par référence" est-il une mauvaise conception ?

Je viens de commencer à apprendre Java et j'ai découvert que la notion de "pass by reference" n'existe pas. Je suis en train de porter une application de C# à Java, et l'application originale a des ints et des doubles qui sont soit des paramètres "ref", soit des paramètres "out".

Au début, j'ai pensé que je pouvais passer un "Integer" ou un "Double", et comme il s'agissait d'un type Reference, la valeur serait modifiée. Mais j'ai ensuite appris que ces types de référence sont immuables.

J'ai donc créé une classe "MutableInteger" et une classe "MutableDouble", et je les ai passées dans mes fonctions. Cela fonctionne, mais je suppose que je vais à l'encontre des intentions initiales de conception du langage.

Le "passage par référence" est-il en général une mauvaise conception ? Comment dois-je changer ma façon de penser ?

Il semble raisonnable de disposer d'une telle fonction :

bool MyClass::changeMyAandB(ref int a, ref int b)
{
    // perform some computation on a and b here

    if (success)
        return true;
    else return false;
}

Est-ce une mauvaise conception ?

16voto

larsmans Points 167484

Ce n'est pas une mauvaise conception dans un langage qui le supporte correctement,(*) mais quand vous devez définir un MutableInt juste pour communiquer entre deux méthodes, il y a sûrement un problème.

La solution pour l'exemple que vous avez posté est de retourner un tableau de deux ints et de signaler l'échec soit par un null ou une exception. Cela ne fonctionne pas toujours, il faut donc parfois...

  • définir des attributs sur l'objet courant (lorsque deux méthodes d'une même classe communiquent) ;
  • passer un tableau d'entiers que la méthode peut modifier (lorsqu'il y a beaucoup d'entiers à passer plusieurs fois) ;
  • créer une classe d'aide, par exemple Result qui encapsule le résultat d'un calcul (lorsqu'il s'agit d'un int et un float au lieu de deux entiers), et peut-être que le calcul proprement dit est une méthode ou un constructeur ;
  • utiliser l'expression idiomatique que vous avez suggérée, mais envisager ensuite d'utiliser le support de cette expression en Apache Commons ou une autre bonne bibliothèque.

(*) Il s'agit simplement d'une mauvaise conception de la langue. En Python ou en Go, vous retourneriez plusieurs valeurs et cesseriez de vous inquiéter.

16voto

Kos Points 29125

La programmation orientée objet est plus efficace si vous structurez votre code en abstractions propres et compréhensibles.

Les nombres, en tant qu'abstraction, sont immuable et ont pas d'identité (c'est-à-dire qu'un "cinq" est toujours un "cinq" et qu'il n'existe pas de "multiples instances de cinq").

Ce que vous essayez d'inventer, c'est un "nombre mutable" qui est mutable et qui a une identité. Ce concept est un peu lourd, et vous feriez probablement mieux de modéliser votre problème à l'aide de des abstractions plus significatives (objets) que cela.

Pensez en termes d'objets qui représentent quelque chose et ont une interface spécifique, plutôt qu'en termes de valeurs individuelles.

6voto

Chris Marisic Points 11495

Passage objets de valeur par référence est en général une mauvaise conception.

Il existe certains scénarios pour lesquels elle est valable, comme la permutation de la position des tableaux pour les opérations de tri à haute performance.

Il y a très peu de raisons pour lesquelles vous devriez avoir besoin de cette fonctionnalité. En C#, l'utilisation du mot-clé OUT est généralement un défaut en soi. Il existe des utilisations acceptables du mot-clé out, telles que DateTime.TryParse(datetext, out DateValue) mais l'utilisation standard des paramètres out est une mauvaise conception logicielle qui souhaite généralement émuler la mauvaise pratique consistant à utiliser des drapeaux d'état pour tout.

3voto

djechelon Points 8092

Le "passage par référence" est-il une mauvaise conception ?

Pas en général. Vous devez comprendre votre scénario spécifique et vous demander ce que fait une fonction. Et vous devez définir correctement votre style de codage, en particulier si vous codez pour d'autres (distribution de bibliothèques).

Le passage par référence est généralement utilisé lorsque votre fonction renvoie plusieurs résultats. Il est souvent judicieux de renvoyer un ResultContainer qui contient toutes les informations renvoyées par la fonction. Prenons l'exemple suivant en C# :

bool isTokenValid(string token, out string username)

VS

AuthenticationResult isTokenValid(string token)

class AuthenticationResult {
    public bool AuthenticationResult;
    public string Username;
}

La différence réside dans le fait que la méthode de référence (dans le cas présent out put) indique clairement qu'il ne peut être utilisé que pour valider un jeton ou, éventuellement, pour extraire des informations sur l'utilisateur. Ainsi, même si vous êtes obligé de passer un paramètre, vous pouvez le jeter si vous n'en avez pas besoin. Le deuxième exemple est plus verbeux.

Bien entendu, le second modèle est préférable si vous disposez d'une telle méthode.

bool doSomething(object a, ref object b, ref object c, ref object d, ... ref object z);

Parce qu'il s'agit d'envelopper tous les produits dans un conteneur.

Permettez-moi de préciser : en Java et en C#, les types non primitifs sont les suivants toujours transmis en tant que référence clonée . Cela signifie que object ne sont pas clonées elles-mêmes, mais seule leur référence est clonée sur la pile, et vous ne pouvez pas vous attendre à pointer sur un objet totalement différent après le retour. Au lieu de cela, il faut toujours s'attendre à ce que l'état de l'objet soit modifié par la méthode. Sinon, il suffit de clone() l'objet et voilà.

Voici donc l'astuce : MutableInteger ou mieux le Holder est la solution pour passer des valeurs primitives par référence.

Il est actuellement utilisé par CORBA idl2java lorsque votre IDL comporte un paramètre de référence.

Dans votre cas spécifique, je ne peux pas vous répondre sur la bonne ou la mauvaise conception parce que la méthode que vous avez montrée est trop générique. Réfléchissez-y. À titre d'exemple, si j'avais une fonction de post-traitement appliquée à des informations multimédias, comme le cryptage, j'utiliserais le passage de référence. Pour moi, la méthode suivante semble être une bonne conception

encrypt(x);

VS

x = encrypt(x);

1voto

djechlin Points 18051

La mauvaise conception dans laquelle vous vous engagez consiste à utiliser un MutableInteger classe. 2 sera toujours 2. Il sera 2 demain.

Le modèle standard Java/OO consiste généralement à faire en sorte que ces éléments soient une instance d'une classe et que cette classe les exploite/gère.

Prochaine étape AtomicInteger . Encore une fois, je n'ai jamais eu de situation dans laquelle j'ai eu besoin de le faire circuler, mais si vous ne voulez pas refactoriser beaucoup de code (votre question était une question de "bonnes pratiques", je devais donc être dur avec vous), c'est une meilleure option. La raison est que si vous laissez un entier s'échapper vers une autre fonction, du point de vue de l'encapsulation, vous ne savez pas si l'autre fonction s'exécutera sur le même thread. Il s'agit donc d'un problème de concurrence et vous voudrez probablement bénéficier de la concurrence offerte par les classes de référence atomiques. (Voir aussi AtomicReference .)

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X