Parfois, nous devons écrire des méthodes qui reçoivent beaucoup d'arguments, par exemple:
public void doSomething(Object objA , Object objectB ,Date date1 ,Date date2 ,String str1 ,String str2 )
{
}
Lorsque je rencontre ce genre de problème, j'encapsule souvent des arguments dans une carte.
Map<Object,Object> params = new HashMap<Object,Object>();
params.put("objA",ObjA) ;
......
public void doSomething(Map<Object,Object> params)
{
// extracting params
Object objA = (Object)params.get("objA");
......
}
Ce n'est pas une bonne pratique, encapsuler des paramètres dans une carte est un gaspillage d'efficacité. La bonne chose est que, la signature propre, facile d'ajouter d'autres paramètres avec le moins de modifications. Quelle est la meilleure pratique pour ce genre de problème?
Dans Effective Java , chapitre 7 (Méthodes), élément 40 (Signer avec soin la méthode de conception), Bloch écrit:
Il existe trois techniques pour raccourcir des listes de paramètres trop longues:
Pour plus de détails, je vous encourage à acheter le livre, ça vaut vraiment le coup.
Utiliser une carte avec des clés magiques est une mauvaise idée. Vous perdez tout temps de vérification de la compilation et il n’est vraiment pas clair quels sont les paramètres requis. Il vous faudra rédiger une documentation très complète pour la rattraper. Vous souviendrez-vous dans quelques semaines de ce que sont ces chaînes sans regarder le code? Et si vous faisiez une faute de frappe? Utilisez le mauvais type? Vous ne le saurez pas avant d'avoir exécuté le code.
Utilisez plutôt un modèle. Faites une classe qui sera un conteneur pour tous ces paramètres. De cette façon, vous gardez le type de sécurité de Java. Vous pouvez également transmettre cet objet à d'autres méthodes, le placer dans des collections, etc.
Bien sûr, si l'ensemble de paramètres n'est pas utilisé ailleurs ou transmis, un modèle dédié risque d'être excessif. Il faut trouver un équilibre, alors faites preuve de bon sens.
Si vous avez plusieurs paramètres facultatifs, vous pouvez créer une API fluide: remplacez une méthode par la chaîne de méthodes
exportWithParams().datesBetween(date1,date2)
.format("xml")
.columns("id","name","phone")
.table("angry_robots")
.invoke();
À l'aide de l'importation statique, vous pouvez créer des API internes fluides:
... .datesBetween(from(date1).to(date2)) ...
Cela s'appelle "Introduce Parameter Object". Si vous passez la même liste de paramètres à plusieurs endroits, créez simplement une classe qui les contient tous.
XXXParameter param = new XXXParameter(objA, objB, date1, date2, str1, str2);
// ...
doSomething(param);
Même si vous ne passez pas souvent la même liste de paramètres en même temps, cette refactorisation simple améliorera néanmoins la lisibilité de votre code, ce qui est toujours bien. Si vous examinez votre code trois mois plus tard, il vous sera plus facile de comprendre quand vous devez corriger un bogue ou ajouter une fonctionnalité.
C'est une philosophie générale bien sûr, et puisque vous n'avez fourni aucun détail, je ne peux pas non plus vous donner de conseils plus détaillés. :-)
Premièrement, je voudrais essayer de refactoriser la méthode. Si vous utilisez autant de paramètres, cela risque d'être trop long. La décomposer améliorerait le code et réduirait potentiellement le nombre de paramètres de chaque méthode. Vous pourriez également être en mesure de refactoriser l'opération entière vers sa propre classe. Deuxièmement, je rechercherais d'autres cas où j'utilise le même (ou sur-ensemble) de la même liste de paramètres. Si vous avez plusieurs instances, cela signifie probablement que ces propriétés vont de pair. Dans ce cas, créez une classe pour contenir les paramètres et utilisez-la. Enfin, j'évaluerais si le nombre de paramètres justifie la création d'un objet de carte pour améliorer la lisibilité du code. Je pense que c’est un appel personnel. Cette solution est pénible dans les deux sens et le point de compromis peut être différent. Pour six paramètres, je ne le ferais probablement pas. Pour 10 je le ferais probablement (si aucune des autres méthodes ne fonctionnait en premier).
C'est souvent un problème lors de la construction d'objets.
Dans ce cas, utilisez modèle d'objet de générateur, cela fonctionne bien si vous avez une grande liste de paramètres et que vous n'avez pas toujours besoin de tous.
Vous pouvez également l'adapter à l'invocation de méthode.
Cela augmente aussi beaucoup la lisibilité.
public class BigObject
{
// public getters
// private setters
public static class Buider
{
private A f1;
private B f2;
private C f3;
private D f4;
private E f5;
public Buider setField1(A f1) { this.f1 = f1; return this; }
public Buider setField2(B f2) { this.f2 = f2; return this; }
public Buider setField3(C f3) { this.f3 = f3; return this; }
public Buider setField4(D f4) { this.f4 = f4; return this; }
public Buider setField5(E f5) { this.f5 = f5; return this; }
public BigObject build()
{
BigObject result = new BigObject();
result.setField1(f1);
result.setField2(f2);
result.setField3(f3);
result.setField4(f4);
result.setField5(f5);
return result;
}
}
}
// Usage:
BigObject boo = new BigObject.Builder()
.setField1(/* whatever */)
.setField2(/* whatever */)
.setField3(/* whatever */)
.setField4(/* whatever */)
.setField5(/* whatever */)
.build();
Vous pouvez également définir une logique de vérification dans les méthodes Builder set .. () et build ().
Il existe un modèle appelé objet Parameter .
L’idée est d’utiliser un objet à la place de tous les paramètres. Maintenant, même si vous devez ajouter des paramètres ultérieurement, il vous suffit de les ajouter à l'objet. L'interface de la méthode reste la même.
Vous pouvez créer une classe pour contenir ces données. Cela doit être suffisamment significatif, mais beaucoup mieux que d'utiliser une carte (OMG).
Code Complete * suggère plusieurs choses:
* Première édition, je sais que je devrais mettre à jour. En outre, il est probable que certains de ces conseils ont peut-être changé depuis la publication de la deuxième édition, alors que OOP commençait à gagner en popularité.
Une bonne pratique serait de refactoriser. Qu'en est-il de ces objets signifie qu'ils devraient être transmis à cette méthode? Devraient-ils être encapsulés dans un seul objet?
Utiliser une carte est un moyen simple de nettoyer la signature d'appel, mais vous avez un autre problème. Vous devez regarder à l'intérieur du corps de la méthode pour voir ce que la méthode attend de cette carte, quels sont les noms de clé ou quels types ont les valeurs.
Une méthode plus simple consisterait à regrouper tous les paramètres dans un objet, mais cela ne résout toujours pas le problème.
Ce que vous avez ici est un problème de conception. Avec plus de 7 paramètres pour une méthode, vous commencerez à avoir des problèmes pour vous rappeler ce qu'ils représentent et leur ordre. À partir de là, vous obtiendrez de nombreux bogues simplement en appelant la méthode dans le mauvais ordre de paramètre.
Vous avez besoin d'une meilleure conception de l'application et non d'une pratique recommandée pour envoyer de nombreux paramètres.
Créez une classe de bean, définissez tous les paramètres (méthode setter) et transmettez cet objet bean à la méthode.
Cela indique souvent que votre classe a plus d’une responsabilité (c’est-à-dire que votre classe en fait trop).
Voir Principe de responsabilité unique
pour plus de détails.
Examinez votre code et voyez pourquoi tous ces paramètres sont transmis. Il est parfois possible de refactoriser la méthode elle-même.
L'utilisation d'une carte rend votre méthode vulnérable. Que se passe-t-il si quelqu'un utilisant votre méthode mal orthographie un nom de paramètre ou poste une chaîne là où votre méthode attend un type défini par l'utilisateur?
Définissez un objet de transfert . Cela vous fournira au minimum une vérification de type; il vous sera peut-être même possible d'effectuer une validation sur le lieu d'utilisation plutôt que dans votre méthode.
Si vous transmettez trop de paramètres, essayez de refactoriser la méthode. Peut-être fait-il beaucoup de choses qu'il n'est pas censé faire. Si ce n'est pas le cas, essayez de remplacer les paramètres par une seule classe. De cette façon, vous pouvez tout encapsuler dans une instance de classe unique et transmettre l'instance, et non les paramètres.