views:

95

answers:

5

I've developed a natural aversion to long parameter lists in functions. While this is to some extent a good thing, sometimes long parameter lists are the lesser of two evils compared to code duplication or ridiculously long functions due to "manual inlining". What's a good way to at least make some of these monstrosities human-readable? For example:

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

This doesn't score high on the readability scale, but four parameters is pretty reasonable in a lot of cases.

+4  A: 

For this kind of situation, I tend to format it like this:

SomeClass[string] someFunction(
    SomeClass!(TemplateParam) foo, 
    string[][string] someAA,
    uint[] dataToProcess,
    SomeEnumType flag
)
{
    // Do stuff.
}
Aaron
I agree. For *very* long parameter lists, the one parameter per line also has it's own problems.... but I've never seen any other solution that works better so long as you need the long parameter list. I also tend to align types and parameter names when I do this ... might make it slightly more readable.
SuperMagic
+1  A: 

I regroup parameters in an (mostly inner) class (or a struct) to avoid wide function declaration/call

Guillaume
I've heard of people doing this, too, but I don't really like the idea of creating a new class or struct just to handle parameters to a function. I might be more okay with that idea if that same parameter list (therefore, the same parameter class/struct) gets used several times. I'm not saying it's necessarily a bad idea, just that I personally don't like it.
Aaron
When I use a third party library and I stumble into a method with many parameters (I remember one with 17 !) with a dozen of booleans flags, I wish the creator of this frankenstein method had use a class to regroup them in one or more classes !
Guillaume
A: 

I like Aaron's reply, just giving a newline for each param.

When that gets to be too much, then it's time to refactor a bit.

If you still need that many parameters, switch to passing in a class that wraps your properties instead. Then you get the added bonus of easily adding defaulted parameters to your method as well, without screwing up your signature.

jvenema
+2  A: 
  • for the sake of readability - put each argument on new line
  • for the sake of usability and better API desgin - group related arguments into new classes, thus shortening the number of arguments.
Bozho
+1  A: 

You can introduce parameter object:

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);
        }
    }
}

Now, your call would look for example:

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

It's much easier to deal with similar cases in languages which allow creation of inline maps:

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

Qualifier final means that a field can be set only from class's constructor. Qualifier static in front of a class means that the class is not tied to it's outer class, i.e. can't access/mutate its fields.

Boris Pavlović
This is absurdly verbose.
dsimcha
dsimcha, you are absolutely right. in a language like Java it's the only waybenefits of this solution is that if you build an API and you don't want to expose constructors or methods with more than three arguments this may be a good thing to have. API consumers will benefit a lot
Boris Pavlović