2010-01-28 6 views
3

J'ai développé une aversion naturelle aux longues listes de paramètres dans les fonctions. Bien que ce soit dans une certaine mesure une bonne chose, parfois les longues listes de paramètres sont le moindre de deux maux comparés à la duplication de code ou à des fonctions ridiculement longues en raison de "l'inlining manuel". Quel est un bon moyen de rendre au moins certaines de ces monstruosités lisibles par l'homme? Par exemple:Comment rendre les longues listes de paramètres lisibles?

SomeClass[string] someFunction(SomeClass!(TemplateParam) foo, 
    string[][string] someAA, uint[] dataToProcess, SomeEnumType flag) { 
    // Do stuff. 
} 

Ceci ne donne pas un score élevé sur l'échelle de lisibilité, mais quatre paramètres sont assez raisonnables dans de nombreux cas.

Répondre

7

Pour ce genre de situation, je tends à formater comme ceci:

SomeClass[string] someFunction(
    SomeClass!(TemplateParam) foo, 
    string[][string] someAA, 
    uint[] dataToProcess, 
    SomeEnumType flag 
) 
{ 
    // Do stuff. 
} 
+0

Je suis d'accord. Pour les listes de paramètres * très * longues, le paramètre par ligne a aussi ses propres problèmes ... mais je n'ai jamais vu d'autre solution qui fonctionne mieux tant que vous avez besoin de la longue liste de paramètres. J'ai aussi tendance à aligner les types et les noms de paramètres quand je fais cela ... pourrait le rendre légèrement plus lisible. – SuperMagic

+0

En outre, si votre liste de paramètres est beaucoup plus longue, c'est une bonne indication que certains refactoring sont en ordre. – Aaron

1

Je regroupe les paramètres dans une classe (la plupart du temps intérieur) (ou un struct) afin d'éviter de large déclaration de fonction/appel

+0

J'ai aussi entendu parler de gens qui font cela, mais je n'aime pas vraiment l'idée de créer une nouvelle classe ou structure juste pour gérer les paramètres d'une fonction. Je pourrais être plus d'accord avec cette idée si cette même liste de paramètres (donc le même paramètre class/struct) est utilisée plusieurs fois. Je ne dis pas que c'est nécessairement une mauvaise idée, mais personnellement je n'aime pas ça. – Aaron

+0

Quand j'utilise une bibliothèque tierce et que je tombe sur une méthode avec de nombreux paramètres (je me souviens d'une avec 17!) Avec une douzaine de drapeaux booléens, je souhaite que le créateur de cette méthode frankenstein utilise une classe pour les regrouper plus de cours! – Guillaume

0

J'aime la réponse d'Aaron, juste en donnant une nouvelle ligne pour chaque paramètre.

Quand cela devient trop, alors il est temps de refactoriser un peu.

Si encore besoin de nombreux paramètres, passez à passer dans une classe qui enveloppe vos propriétés à la place. Ensuite, vous obtenez le bonus supplémentaire d'ajouter facilement des paramètres par défaut à votre méthode, sans falsifier votre signature.

2
  • pour des raisons de lisibilité - mettre chaque argument sur une nouvelle ligne
  • par souci de facilité d'utilisation et une meilleure API desgin - groupe arguments liés à de nouvelles classes, réduisant ainsi le nombre d'arguments.
1

Vous pouvez introduire objet paramètre:

class ParameterObject { 
    public final SomeClass!(TemplateParam) foo; 
    public final string[][string] someAA; 
    public final uint[] dataToProcess; 
    public final SomeEnumType flag; 

    private ParameterObject(
     SomeClass!(TemplateParam) foo, 
     string[][string] someAA, 
     uint[] dataToProcess, 
     SomeEnumType flag) { 
     this.foo = foo; 
     this.someAA = someAA; 
     this.dataToProcess = dataToProcess; 
     this.flag = flag; 
    } 

    private static class Builder { 
     public SomeClass!(TemplateParam) foo; 
     public string[][string] someAA; 
     public uint[] dataToProcess; 
     public SomeEnumType flag; 

     public Builder foo(SomeClass!(TemplateParam) foo) { 
      this.foo = foo; 
      return this; 
     } 

     public Builder someAA(string[][string] someAA) { 
      this.someAA = someAA; 
      return this; 
     } 

     public Builder dataToProcess(uint[] dataToProcess) { 
      this.dataToProcess = dataToProcess; 
      return this; 
     } 

     public Builder flag(SomeEnumType flag) { 
      this.flag = flag; 
      return this; 
     } 

     public ParameterObject build() { 
      if (null == foo) throw Exception("init foo!"); 
      if (null == someAA) throw Exception("init someAA!"); 
      if (null == dataToProcess) throw Exception("init dataToProcess!"); 
      if (null == flag) throw Exception("init flag!"); 
      return new ParameterObject(foo, someAA, dataToProcess, flag); 
     } 
    } 
} 

Maintenant, votre appel regarderait par exemple:

SomeClass[string] myValue = 
    someFunction(
     new ParameterObject.Build(). 
      foo(myFoo). 
      someAA(myAA). 
      dataToProcess(myData). 
      flag(false). 
      build() 
    ); 

Il est beaucoup plus facile de traiter des cas similaires dans les langues qui permettent la création de ligne cartes:

someFunction(
    Map.new(
     foo => myFoo, 
     someAA => myAA, 
     dataToProcess => myData, 
     flag => false 
    ) 

Qualificateur final signifie qu'un champ peut être défini uniquement à partir du constructeur de la classe. Le qualificateur static placé devant une classe signifie que la classe n'est pas liée à sa classe externe, c'est-à-dire qu'elle ne peut pas accéder/transformer ses champs.

+0

C'est absurdement verbeux. – dsimcha

+0

dsimcha, vous avez absolument raison. Dans un langage comme Java, la seule façon de profiter de cette solution est que si vous construisez une API et que vous ne voulez pas exposer des constructeurs ou des méthodes avec plus de trois arguments, cela peut être une bonne chose à avoir. Les consommateurs de l'API bénéficieront beaucoup –

+1

S'il vous plaît ne laissez pas le point à la fin de l'expression originale, c'est bizarre. La ligne commençant par un point indique clairement qu'il s'agit d'un appel de continuation de la ligne précédente. –

Questions connexes