web-dev-qa-db-fra.com

La modification d'un paramètre entrant est-elle un contre-modèle?

Je programme en Java et je fais toujours des convertisseurs comme ceci:

public OtherObject MyObject2OtherObject(MyObject mo){
    ... Do the conversion
    return otherObject;
}

Sur le nouveau lieu de travail, le schéma est le suivant:

public void MyObject2OtherObject(MyObject mo, OtherObject oo){
    ... Do the conversion
}

Pour moi, c'est un peu malodorant, car je me suis habitué à ne pas changer les paramètres entrants. Cette modification des paramètres entrants est-elle un contre-modèle ou est-ce correct? At-il de sérieux inconvénients?

64
CsBalazsHungary

Ce n'est pas un contre-modèle, c'est une mauvaise pratique.

La différence entre un antipattern et une simple mauvaise pratique est ici: définition anti-modèle .

Le nouveau style de travail que vous montrez est une mauvaise pratique , temps résiduel ou pré-POO, selon le code propre de l'oncle Bob.

Les arguments sont le plus naturellement interprétés comme des entrées dans une fonction.

Tout ce qui vous oblige à vérifier la signature de la fonction équivaut à une double prise. C’est une rupture cognitive et doit être évitée. Avant la programmation orientée objet, il était parfois nécessaire d'avoir des arguments de sortie. Cependant, une grande partie du besoin d'arguments de sortie disparaît dans les langues OO

73
Tulains Córdova

Citant le célèbre livre de Robert C. Martin "Clean Code":

Les arguments de sortie doivent être évités

Les fonctions doivent avoir un petit nombre d'arguments

Le deuxième modèle viole les deux règles, en particulier celle de l '"argument de sortie". En ce sens, c'est pire que le premier schéma.

17
claasz

Le deuxième idiome peut être plus rapide, car l'appelant peut réutiliser une variable sur une longue boucle, au lieu que chaque itération crée une nouvelle instance.

Je ne l'utiliserais généralement pas, mais par exemple. dans la programmation de jeux, il a sa place. Par exemple, regardez Vector3f de JavaMonkey beaucoup d'opérations permettent de passer l'instance qui doit être modifiée et renvoyée comme résultat.

15
user470365

Je ne pense pas que ce sont deux morceaux de code équivalents. Dans le premier cas, vous devez créer le otherObject. Vous pouvez modifier l'instance existante dans la seconde. Les deux ont leurs utilisations. L'odeur de code serait préférable l'un à l'autre.

10
Euphoric

Cela dépend vraiment de la langue.

Dans Java la deuxième forme peut être un anti-modèle, mais certains langages traitent le passage de paramètres différemment. Dans Ada et VHDL par exemple, au lieu de passer par valeur ou par référence, les paramètres peuvent avoir des modes in, out ou in out.

C'est une erreur de modifier un paramètre in ou de lire un paramètre out, mais toute modification apportée à un in out le paramètre est renvoyé à l'appelant.

Donc, les deux formes dans Ada (ce sont aussi des VHDL légaux) sont

function MyObject2OtherObject(mo : in MyObject) return OtherObject is
begin
    ... Do the conversion
    return otherObject;
end MyObject2OtherObject;

et

procedure MyObject2OtherObject(mo : in MyObject; oo : out OtherObject) is
begin
    ... Do the conversion
    oo := ... the conversion result;
end MyObject2OtherObject;

Les deux ont leur utilité; une procédure peut renvoyer plusieurs valeurs dans plusieurs paramètres Out tandis qu'une fonction ne peut renvoyer qu'un seul résultat. Étant donné que l'objectif du deuxième paramètre est clairement indiqué dans la déclaration de procédure, il ne peut y avoir d'objection à ce formulaire. J'ai tendance à préférer la fonction pour la lisibilité. mais il y aura des cas où la procédure sera meilleure, par ex. où l'appelant a déjà créé l'objet.

7
Brian Drummond

L'avantage du second modèle est qu'il oblige l'appelant à s'approprier et à assumer la responsabilité de l'objet créé. Il ne fait aucun doute que la méthode a créé l'objet ou l'a obtenu à partir d'un pool réutilisable. L'appelant sait qu'il est responsable de la durée de vie et de l'élimination du nouvel objet.

Les inconvénients de cette méthode sont:

  1. Ne peut pas être utilisé comme méthode d'usine, l'appelant doit connaître le sous-type exact de OtherObject dont il a besoin et le construire à l'avance.
  2. Ne peut pas être utilisé avec des objets qui nécessitent des paramètres dans leur constructeur si ces paramètres proviennent de MyObject. OtherObject doit être constructible sans connaître MyObject.

La réponse est basée sur mon expérience en c #, j'espère que la logique se traduit en Java.

3
Rotem

Il semble en effet malodorant, et sans voir plus de contexte, il est impossible de dire avec certitude. Il peut y avoir deux raisons à cela, bien qu'il existe des alternatives pour les deux.

Tout d'abord, c'est une manière concise d'implémenter une conversion partielle ou de laisser le résultat avec des valeurs par défaut si la conversion échoue. Autrement dit, vous pourriez avoir ceci:

public void ConvertFoo(Foo from, Foo to) {
    if (can't convert) {
        return;
    }
    ...
}

Foo a;
Foo b = DefaultFoo();
ConvertFoo(a, b);
// If conversion fails, b is unchanged

Bien sûr, cela est généralement géré à l'aide d'exceptions. Cependant, même si des exceptions doivent être évitées pour une raison quelconque, il existe une meilleure façon de le faire - le modèle TryParse étant une option.

Une autre raison est que cela pourrait être pour des raisons de cohérence, par exemple, il fait partie d'une API publique où cette méthode est utilisée pour toutes les fonctions de conversion pour une raison quelconque (comme d'autres fonctions de conversion ayant plusieurs sorties).

Java n'est pas génial pour gérer plusieurs sorties - il ne peut pas avoir de paramètres de sortie uniquement comme certains langages, ou avoir plusieurs valeurs de retour comme d'autres - mais même quand même, vous pouvez utiliser des objets de retour.

La raison de la cohérence est plutôt boiteuse mais malheureusement, elle peut être la plus courante.

  • Peut-être que les flics de style sur votre lieu de travail (ou votre base de code) viennent d'un arrière-plan non Java et ont hésité à changer.
  • Votre code peut avoir été un port d'une langue où ce style est plus idiomatique.
  • Votre organisation peut avoir besoin de maintenir la cohérence de l'API dans différentes langues, et c'était le style au plus petit dénominateur commun (c'est idiot mais ça arrive même pour Google ).
  • Ou peut-être que le style avait plus de sens dans un passé lointain et s'est transformé en sa forme actuelle (par exemple, il pourrait s'agir du modèle TryParse mais un prédécesseur bien intentionné a supprimé la valeur de retour après avoir découvert que personne ne l'a vérifiée).
3
congusbongus

Étant donné la sémantique lâche - myObjectToOtherObject pourrait également signifier que vous déplacez les données some du premier objet vers le second ou que vous les convertissez entièrement à nouveau, la deuxième méthode semble plus adéquate.

Cependant, si le nom de la méthode était Convert (ce qui devrait être le cas si nous regardons la partie "... faire la conversion"), je dirais que la deuxième méthode n'aurait aucun sens. Vous ne convertissez pas une valeur en une autre valeur qui existe déjà, IMO.

2
guillaume31