tags:

views:

1318

answers:

22

Occasionally I come across methods with an uncomfortable number of parameters. More often than not, they seem to be constructors. It seems like there ought to be a better way, but I can't see what it is.

return new Shniz(foo, bar, baz, quux, fred, wilma, barney, dino, donkey)

I've thought of using structs to represent the list of parameters, but that just seems to shift the problem from one place to another, and create another type in the process.

ShnizArgs args = new ShnizArgs(foo, bar, baz, quux, fred, wilma, barney, dino, donkey)
return new Shniz(args);

So that doesn't seem like an improvement. So what is the best approach?

+17  A: 

The best way would be to find ways to group the arguments together. This assumes, and really only works if, you would end up with multiple "groupings" of arguments.

For instance, if you are passing the specification for a rectangle, you can pass x, y, width, and height or you could just pass a rectangle object that contains x, y, width, and height.

Look for things like this when refactoring to clean it up somewhat. If the arguments really can't be combined, start looking at whether you have a violation of the Single Responsibility Principle.

Matthew Brubaker
Good idea but bad example; the constructor for the Rectangle would have to have 4 arguments. This would make more sense if the method was expecting 2 sets of rectangle coordinates/dimensions. Then you could pass 2 rectangles instead of x1, x2, y1, y2...
Outlaw Programmer
Fair enough. Like I said, it really only makes sense to do if you end up with multiple logical groupings.
Matthew Brubaker
+1: To Single Responsibility, its one of the few comments in all the answers that is really addressing the true issue. What object really needs 7 independant values to form its identity.
AnthonyWJones
+2  A: 

You can try to group your parameter into multiples meaningful struct/class (if possible).

madgnome
+1  A: 

I think the method you described is the way to go. When I find a method with a lot of parameters and/or one that is likely to need more in the future, I usually create a ShnizParams object to pass through, like you describe.

Instantsoup
+2  A: 

I would generally lean towards the structs approach - presumably the majority of these parameters are related in some way and represent the state of some element that is relevant to your method.

If the set of parameters can't be made into a meaningful object, that's probably a sign that Shniz is doing too much, and the refactoring should involve breaking the method down into separate concerns.

Andrzej Doyle
+11  A: 

If it's a constructor, particularly if there are multiple overloaded variants, you should look at the Builder pattern:

Foo foo = new Foo()
          .configBar(anything)
          .configBaz(something, somethingElse)
          // and so on

If it's a normal method, you should think about the relationships between the values being passed, and perhaps create a Transfer Object.

kdgregory
Excellent reply. Perhaps even more relevant than the "put the parameters in a class" reply that everyone (including me) gave.
Wouter Lievens
It's probably a bad idea to make your class mutable just to avoid passing in too many parameters to the constructor.
Outlaw Programmer
@outlaw - if mutability is a concern, you can easily implement "run once" semantics. However, a large number of ctor params often indicates a need for configuration (or, as others have noted, a class trying to do too many things). (cont)
kdgregory
kdgregory
I like the builder pattern, but I separate my immutable and mutable builder types, like string/StringBuilder, but I use nested classes: Foo / Foo.Builder. I have a PowerShell script to generate the code to do this for simple data classes.
Jay Bazuzi
+6  A: 

The classic answer to this is to use a class to encapsulate some, or all, of the parameters. In theory that sounds great, but I'm the kind of guy who creates classes for concepts that have meaning in the domain, so it's not always easy to apply this advice.

E.g. instead of:

driver.connect(host, user, pass)

You could use

config = new Configuration()
config.setHost(host)
config.setUser(user)
config.setPass(pass)
driver.connect(config)

YMMV

Wouter Lievens
I'd definitely like the first piece of code more. I agree, that there is a certain limit, above which the numbe rof parameters becomes ugly, but for my taste, 3 would be acceptable.
blabla999
+1  A: 

How about not setting it in all at once at the constructors but doing it via properties/setters? I have seen some .NET classes that utilize this approach such as Process class:

        Process p = new Process();

        p.StartInfo.UseShellExecute = false;
        p.StartInfo.CreateNoWindow = true;
        p.StartInfo.RedirectStandardOutput = true;
        p.StartInfo.RedirectStandardError = true;
        p.StartInfo.FileName = "cmd";
        p.StartInfo.Arguments = "/c dir";
        p.Start();
m3rLinEz
C# 3 actually has a syntax for doing this easily: object initializers.
Daren Thomas
A: 

It depends on what kind of arguments you have, but if they are a lot of boolean values/options maybe you could use a Flag Enum?

scottm
A: 

You can trade complexity for source code lines. If the method itself does too much (Swiss knife) try to halve its tasks by creating another method. If the method is simple only it needs too many parameters then the so called parameter objects are the way to go.

+4  A: 

I would use the default constructor and property settors. C# 3.0 has some nice syntax to do this automagically.

return new Shniz { Foo = foo,
                   Bar = bar,
                   Baz = baz,
                   Quuz = quux,
                   Fred = fred,
                   Wilma = wilma,
                   Barney = barney,
                   Dino = dino,
                   Donkey = donkey
                 };

The code improvement comes in simplifying the constructor and not having to support multiple methods to support various combinations. The "calling" syntax is still a little "wordy", but not really any worse than calling the property settors manually.

tvanfosson
This would allow an object t new Shniz() to exist. A good OO implementation would seek to minimise the possibility of objects existing incomplete state.
AnthonyWJones
In general, any language with a native hash/dictionary syntax comes with an adequate substitute for named parameters (which are great and often what these situations call for, but for some reason the only popular language supporting them is the worst one on the planet).
chaos
@chaos: which language do you have in mind?
recursive
+4  A: 

I don't want to sound like a wise-crack, but you should also check to make sure the data you are passing around really should be passed around: Passing stuff to a constructor (or method for that matter) smells a bit like to little emphasis on the behavior of an object.

Don't get me wrong: Methods and constructors will have a lot of parameters sometimes. But when encountered, do try to consider encapsulating data with behavior instead.

This kind of smell (since we are talking about refactoring, this horrible word seems appropriate...) might also be detected for objects that have a lot (read: any) properties or getters/setters.

Daren Thomas
A: 

I think that problem is deeply tied to the domain of the problem you're trying to solve with the class.

In some cases, a 7-parameter constructor may indicate a bad class hierarchy: in that case, the helper struct/class suggested above is usually a good approach, but then you also tend to end up with loads of structs which are just property bags and don't do anything useful. The 8-argument constructor might also indicate that your class is too generic / too all-purpose so it needs a lot of options to be really useful. In that case you can either refactor the class or implement static constructors that hide the real complex constructors: eg. Shniz.NewBaz (foo, bar) could actually call the real constructor passing the right parameters.

axel_c
A: 

One consideration is which of the values would be read-only once the object is created?

Publicly writable properties could perhaps be assigned after construction.

Where ultimately do the values come from? Perhaps some values are truely external where as others are really from some configuration or global data that is maintained by the library.

In this case you could conceal the constructor from external use and provide a Create function for it. The create function takes the truely external values and constructs the object, then uses accessors only avaiable to the library to complete the creation of the object.

It would be really strange to have an object that requires 7 or more parameters to give the object a complete state and all truely being external in nature.

AnthonyWJones
+1  A: 

If some of the constructor parameters are optional it makes sense to use a builder, which would get the required parameters in the constructor, and have methods for the optional ones, returning the builder, to be used like this:

return new Shniz.Builder(foo, bar).baz(baz).quux(quux).build();

The details of this are described in Effective Java, 2nd Ed., p. 11. For method arguments, the same book (p. 189) describes three approaches for shortening parameter lists:

  • Break the method into multiple methods that take fewer arguments
  • Create static helper member classes to represent groups of parameters, i.e. pass a DinoDonkey instead of dino and donkey
  • If parameters are optional, the builder above can be adopted for methods, defining an object for all parameters, setting the required ones and then calling some execute method on it
Fabian Steeg
A: 

When a clas has a constructor that takes too many arguments, it is usually a sign that it has too many responsibilities. It can probably be broken into separate classes that cooperate to give the same functionalities.

In case you really need that many arguments to a constructor, the Builder pattern can help you. The goal is to still pass all the arguments to the constructor, so its state is initialized from the start and you can still make the class immutable if needed.

See below :

public class Toto {
    private final String state0;
    private final String state1;
    private final String state2;
    private final String state3;

    public Toto(String arg0, String arg1, String arg2, String arg3) {
        this.state0 = arg0;
        this.state1 = arg1;
        this.state2 = arg2;
        this.state3 = arg3;
    }

    public static class TotoBuilder {
        private String arg0;
        private String arg1;
        private String arg2;
        private String arg3;

        public TotoBuilder addArg0(String arg) {
            this.arg0 = arg;
            return this;
        }
        public TotoBuilder addArg1(String arg) {
            this.arg1 = arg;
            return this;
        }
        public TotoBuilder addArg2(String arg) {
            this.arg2 = arg;
            return this;
        }
        public TotoBuilder addArg3(String arg) {
            this.arg3 = arg;
            return this;
        }

        public Toto newInstance() {
            // maybe add some validation ...
            return new Toto(this.arg0, this.arg1, this.arg2, this.arg3);
        }
    }

    public static void main(String[] args) {
        Toto toto = new TotoBuilder()
            .addArg0("0")
            .addArg1("1")
            .addArg2("2")
            .addArg3("3")
            .newInstance();
    }

}
Guillaume
+2  A: 

This is quoted from Fowler and Beck book: "Refactoring"

Long Parameter List

In our early programming days we were taught to pass in as parameters everything needed by a routine. This was understandable because the alternative was global data, and global data is evil and usually painful. Objects change this situation because if you don't have something you need, you can always ask another object to get it for you. Thus with objects you don't pass in everything the method needs; instead you pass enough so that the method can get to everything it needs. A lot of what a method needs is available on the method's host class. In object-oriented programs parameter lists tend to be much smaller than in traditional programs. This is good because long parameter lists are hard to understand, because they become inconsistent and difficult to use, and because you are forever changing them as you need more data. Most changes are removed by passing objects because you are much more likely to need to make only a couple of requests to get at a new piece of data. Use Replace Parameter with Method when you can get the data in one parameter by making a request of an object you already know about. This object might be a field or it might be another parameter. Use Preserve Whole Object to take a bunch of data gleaned from an object and replace it with the object itself. If you have several data items with no logical object, use Introduce Parameter Object. There is one important exception to making these changes. This is when you explicitly do not want to create a dependency from the called object to the larger object. In those cases unpacking data and sending it along as parameters is reasonable, but pay attention to the pain involved. If the parameter list is too long or changes too often, you need to rethink your dependency structure.

Youssef
+1  A: 

If your language supports it, use named parameters and make as many optional (with reasonable defaults) as possible.

+1  A: 

I concur with the approach of moving the parameters into a parameter object (struct). Rather than just sticking them all in one object though, review if other functions use similar groups of parameters. A paramater object is more valuable if its used with multiple functions where you expect that set of parameters to change consistently across those functions. It may be that you only put some of the parameters into the new parameter object.

Frank Schwieterman
+1  A: 

If you have that many parameters, chances are that the method is doing too much, so address this first by splitting the method into several smaller methods. If you still have too many parameters after this try grouping the arguments or turning some of the parameters into instance members.

Prefer small classes/methods over large. Remember the single responsibility principle.

Brian Rasmussen
The problem with instance members and properties is that they 1) must be writable, 2) might not be set. In the case of a constructor, there are certain fields that I want to ensure are filled before an instance is allowed to exist.
recursive
@recursive - I disagree that fields/properties always have to be writable. For small classes there are plenty of times where readonly members make sense.
Brian Rasmussen
+5  A: 

I'm going to assume you mean C#. Some of these things apply to other languages, too.

You have several options:

switch from constructor to property setters. This can make code more readable, because it's obvious to the reader which value corresponds to which parameters. Object Initializer syntax makes this look nice. It's also simple to implement, since you can just use auto-generated properties and skip writing the constructors.

class C
{
    public string S { get; set; }
    public int I { get; set; }
}

new C { S = "hi", I = 3 };

However, you lose immutability, and you lose the ability to ensure that the required values are set before using the object at compile time.

Builder Pattern.

Think about the relationship between string and StringBuilder. You can get this for your own classes. I like to implement it as a nested class, so class C has related class C.Builder. I also like a fluent interface on the builder. Done right, you can get syntax like this:

C c = new C.Builder()
    .SetX(4) // SetX is the fluent equivalent to a property setter
    .SetY("hello")
    .ToC();  // ToC is the builder pattern analog to ToString()

// Modify without breaking immutability
c = c.ToBuilder().SetX(2).ToC();

// Still useful to have a traditional ctor:
c = new C(1, "...");

// And object initializer syntax is still available:
c = new C.Builder { X = 4, Y = "boing" }.ToC();

I have a PowerShell script that lets me generate the builder code to do all this, where the input looks like:

class C {
    field I X
    field string Y
}

So I can generate at compile time. partial classes let me extend both the main class and the builder without modifying the generated code.

"Introduce Parameter Object" refactoring. See the Refactoring Catalog. The idea is that you take some of the parameters you're passing and put them in to a new type, and then pass an instance of that type instead. If you do this without thinking, you will end up back where you started:

new C(a, b, c, d);

becomes

new C(new D(a, b, c, d));

However, this approach has the greatest potential to make a positive impact on your code. So, continue by following these steps:

  1. Look for subsets of parameters that make sense together. Just mindlessly grouping all parameters of a function together doesn't get you much; the goal is to have groupings that make sense. You'll know you got it right when the name of the new type is obvious.

  2. Look for other places where these values are used together, and use the new type there, too. Chances are, when you've found a good new type for a set of values that you already use all over the place, that new type will make sense in all those places, too.

  3. Look for functionality that is in the existing code, but belongs on the new type.

For example, maybe you see some code that looks like:

bool SpeedIsAcceptable(int minSpeed, int maxSpeed, int currentSpeed)
{
    return currentSpeed >= minSpeed & currentSpeed < maxSpeed;
}

You could take the minSpeed and maxSpeed parameters and put them in a new type:

class SpeedRange
{
   public int Min;
   public int Max;
}

bool SpeedIsAcceptable(SpeedRange sr, int currentSpeed)
{
    return currentSpeed >= sr.Min & currentSpeed < sr.Max;
}

This is better, but to really take advantage of the new type, move the comparisons into the new type:

class SpeedRange
{
   public int Min;
   public int Max;

   bool Contains(int speed)
   {
       return speed >= min & speed < Max;
   }
}

bool SpeedIsAcceptable(SpeedRange sr, int currentSpeed)
{
    return sr.Contains(currentSpeed);
}

And now we're getting somewhere: the implementation of SpeedIsAcceptable() now says what you mean, and you have a useful, reusable class. (The next obvious step is to make SpeedRange in to `Range'.)

As you can see, Introduce Parameter Object was a good start, but its real value was that it helped us discover a useful type that has been missing from our model.

Jay Bazuzi
I would suggest trying "Introduce Parameter Object" first, and only fallback to the other options if you can't find a good parameter object to create.
Douglas Leeder
+1  A: 

You haven't provided enough information to warrant a good answer. A long parameter list isn't inherently bad.

Shniz(foo, bar, baz, quux, fred, wilma, barney, dino, donkey)

could be interpreted as:

void Shniz(int foo, int bar, int baz, int quux, int fred, 
           int wilma, int barney, int dino, int donkey) { ...

In this case you're far better off to create a class to encapsulate the parameters because you give meaning to the different parameters in a way that the compiler can check as well as visually making the code easier to read. It also makes it easier to read and refactor later.

// old way
Shniz(1,2,3,2,3,2,1,2);
Shniz(1,2,2,3,3,2,1,2); 

//versus
ShnizParam p = new ShnizParam { Foo = 1, Bar = 2, Baz = 3 };
Shniz(p);

Alternatively if you had:

void Shniz(Foo foo, Bar bar, Baz baz, Quux quux, Fred fred, 
           Wilma wilma, Barney barney, Dino dino, Donkey donkey) { ...

This is a far different case because all the objects are different (and aren't likely to be muddled up). Agreed that if all objects are necessary, and they're all different, it makes little sense to create a parameter class.

Additionally, are some parameters optional? Are there method override's (same method name, but different method signatures?) These sorts of details all matter as to what the best answer is.

* A property bag can be useful as well, but not specifically better given that there is no background given.

As you can see, there is more than 1 correct answer to this question. Take your pick.

Robert Paulson
+1  A: 

Named arguments are a good option (presuming a language which supports them) for disambiguating long (or even short!) parameter lists while also allowing (in the case of constructors) the class's properties to be immutable without imposing a requirement for allowing it to exist in a partially-constructed state.

The other option I would look for in doing this sort of refactor would be groups of related parameters which might be better handled as an independent object. Using the Rectangle class from an earlier answer as an example, the constructor which takes parameters for x, y, height, and width could factor x and y out into a Point object, allowing you to pass three parameters to the Rectangle's constructor. Or go a little further and make it two parameters (UpperLeftPoint, LowerRightPoint), but that would be a more radical refactoring.

Dave Sherohman