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?
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
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.
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.
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.
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.
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:
OtherObject
dont il a besoin et le construire à l'avance.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.
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.
É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.