Long Parameter List

Long parameter lists should be considered a GoodThing (TM). They are essentials in building powerful libraries and reusable frameworks. And we have a long list of empirical evidence that LongParameterLists are essential and unavoidable

Take any Unix command at random, even the simplest one, and run command --help or man command. For example: cp --help. On linux it shows two screens full of options:

 Usage: cp [OPTION]... SOURCE DEST
  or:  cp [OPTION]... --target-directory=DIRECTORY SOURCE...
 Copy SOURCE to DEST, or multiple SOURCE(s) to DIRECTORY.

-a, --archive same as -dpR --backup[=CONTROL] make a backup of each existing destination file -b like --backup but does not accept an argument -d, --no-dereference never follow symbolic links -f, --force if an existing destination file cannot be opened, remove it and try again -i, --interactive prompt before overwrite -H follow command-line symbolic links -l, --link link files instead of copying -L, --dereference always follow symbolic links -p, --preserve preserve file attributes if possible --parents append source path to DIRECTORY -P same as `--parents' for now; soon to change to `--no-dereference' to conform to POSIX -r copy recursively, non-directories as files WARNING: use -R instead when you might copy special files like FIFOs or /dev/zero --remove-destination remove each existing destination file before attempting to open it (contrast with --force) --sparse=WHEN control creation of sparse files -R, --recursive copy directories recursively --strip-trailing-slashes remove any trailing slashes from each SOURCE argument -s, --symbolic-link make symbolic links instead of copying -S, --suffix=SUFFIX override the usual backup suffix --target-directory=DIRECTORY move all SOURCE arguments into DIRECTORY -u, --update copy only when the SOURCE file is newer than the destination file or when the destination file is missing -v, --verbose explain what is being done -x, --one-file-system stay on this file system --help display this help and exit --version output version information and exit

By default, sparse SOURCE files are detected by a crude heuristic and the corresponding DEST file is made sparse as well. That is the behavior selected by --sparse=auto. Specify --sparse=always to create a sparse DEST file whenever the SOURCE file contains a long enough sequence of zero bytes. Use --sparse=never to inhibit creation of sparse files.

Very powerful, flexible, and strictly necessary.

In OO frameworks, take for example the ACE C++ framework from DougSchmidt. You'll see many constructors in there that have more than six parameters. Or take the SmartPointer class from ModernCeePlusPlusDesign. It has four templatized arguments. Basically for a smart pointer which is one of the smallest interfaces you can have in C++.

That is so because the more abstract, the more usable a piece of code is, the more likely it is that it will need to be adapted to various local situations. In order to factor out commonalities, but still allow it to be used in exceptional cases, you create a LongParameterList that allows the caller to adapt the abstraction to its local needs.

When people complain that this puts too much of a burden on the caller, it shows that people haven't programmed too much in advanced languages like VisualBasic, ModulaThree, Perl or Unix shells. Basically that's a non-issue: you combine parameter passing by name and provide sensible default values for most of them, so that client code that doesn't need fine tuning doesn't need to suffer the penalty either. Or clients that need fine tuning in one aspect but not all the aspects can set only those parameters that are important for them.

For example:
    cp source target
    cp --recursive source target
    cp --recursive --dereference --one-file-system source target

In a statically typed language the last one would look like:
   copy ( sourcedir='source', targetdir='target', recursive=true, dereference=true, stayInOneFS=true);

Now I know that LongParameterList hurts in Java, CeePlusPlus (less than in Java) and the likes. But that is a language design deficiency, and can be solved (albeit with a bit more effort) by doing what I call EmulateKeywordAndDefaultParameters:

    new CopyOperation?(source,target)

It almost looks as good as in VisualBasic, except it goes against the GrainOfTheLanguage, but then that language doesn't have a lot of grain, anyways.

-- CostinCozianu

One of the CodeSmells described in RefactoringImprovingTheDesignOfExistingCode. A method that has too many parameters is asking to be refactored.

One reason is that a LongParameterList increases the likelihood that two or more parameters will be of the same type/class, which may lead to coding of invalid calls.

See also: KeywordParameterPassing

This smell is quite unpleasant. -- BeenThereDoneThat

Fixing it will depend on how the parameters can be clustered together, but it's quite likely that you can separate them into two or three logical groups. Make a class that represents each group, or at least a struct.

  1. BigBangRefactoring? to tear everything apart and put it back together the new way. You will FearNoCode? if you have the UnitTests.
  2. Doing it more gradually is less fun.
    1. Make yet another new method, which takes the new objects and calls the old methods.
    2. You now have backwards compatibility, and therefore breathing space.
    3. Go round deprecating or changing calls until they're all gone.

Long parameter lists look like this: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowfunctions/createwindow.asp

At least the parameter types in this sample have comprehensible and easily pronounced names like "LPCTSTR".

Opinions on LPLs differ, obviously, but sane people agree that they are Pure Evil(tm).

Naming parameters in a call only solves one problem with LPLs, namely, that the function call would become unreadable. In combination with DefaultValues for parameters it also solves a second problem, namely, that the caller has to specify every parameter even if sensible defaults would do. Most fundamental problems with LPLs cannot be cured this way, though:
 void Master::doSomething(int a, int b, Foo foo, Bar bar, Moo moo, Meow meow) {
 	slave.doSomething(a, b, foo, bar, moo, meow);
While breakage can occur when shorter function signatures are used, the problem tends to be most rampant the longer signatures get. This is because longer signatures are often used when SignatureEntropy? is high. All of the above problems can be solved nicely using ParameterObjects. Their usage typically does not guarantee BinaryCompatibility, but it does help a lot in maintaining SourceCompatibility?.

This piece of immature wise-cracking was brought to you by ArneVogel.

I can see a lot of handwaving in the above. Incidentally the piece of code that you refer to has kept BinaryCompatibility for over a decade now, so it is a rather spectacular example of how a well designed API for a rather complex domain can make use of LongParameterList and provide an impressive array of power and functionality while keeping it stable for over a decade. Try to find a contemporary Linux distro out there that runs binaries from a decade ago, and good luck. Or how about if you find a device driver for Linux written in 2000 that runs on current kernel. Folks can bitch all they want about Windows, but if you look objectively it is an impressive engineering achievement.

ParameterObjects don't solve anything, they just move complexity some place else, while adding unnecessary entities to the system. You can group stuff that belongs together in parameter object, but not all parameters in a LongParameterList can be sensibly crammed together in parameter objects. If you want to make your case you can show us how you'd design a better public interface for the cp command in Unix, or a better interface for the CreateWindow function under windows, and then we'll see. --CostinCozianu

Hmmm... In CreateWindow?,
    int x,
    int y,
    int nWidth,
    int nHeight,
looks like a "Rectangle" to me.

Ok, so now I as a programmer I'd have to replace
  CreateWindow? ( ..., 100,100, 300, 200, ...) 
  struct Rect r;
  r.x= 100;
  r.y= 100;
  r.nWidth= 300;
  r.nHeight= 400;
  CreateWindow?( ..., &r, ...);
I don't know if I'm so happy with the later.It's true that the Rectangle case can go either way. If you already had a rectangle in the calling context, you can pass it more easily. Rectangles can also be subject to geometrical transformations as one object, far easier than 4 separate variables. On the other hand, if you had 4 separate variables or constants like the above, or if you cared about performance, say you wanted to draw tons of rectangles in a game, than the unfolded variables may be arguably better. Some frameworks choose to provide all options in one overloaded function where the language supports that, while Microsoft SDK goes consistently with the unfolded four variables also in part because the language is C and because folks who need the folded version can easily work around the unfolded design, while the other way around does not work.

It's not clear cut, but even with the folded rectangle, CreateWindow? would still have 8 parameters while CreateWindowEx? would have 9.

Microsoft APIs are rarely that consistent (the Win32 API was formed by accretion more than by design). [??? -- rather dubious claim]

For every function like CreateWindow? that has a long hairy parameter list, I can think of another function that takes a single (hairy) struct. Sometimes this is because the call ends up going through the narrow SendMessage? pipe where you are only allowed a couple of parameters.

Also, if using CeePlusPlus, you could choose to create the rectangle structure automatically using a convenience constructor if that is more concise:

 CreateWindow?( ..., CRect(100,100,300,400), ...);

So what would the inner CRect(100,100,300,400) accomplish ? I am affraid that not much.

[It tells the compiler, as well as future maintainers, what those 4 numbers sandwiched in there stand for.]

Replacing a LongParameterList with an equally complex ParameterObject is something I would definitely recommend against, unless it is part of EmulateKeywordAndDefaultParameters, in which case we're still talking in essence of a LongParametersList? but with a workaround to language limitations. One needs to decouple the technical details related to ProgramIntoaLanguage, from what is really happening. Even with parameter objects, we're still talking about one logical operation (like creating a window) that depends on a long list of variables, because it needs to be adapted to a wide variety of contexts.

But with keyword and default parameters (either for real or emulated), I contend that LongParameterList are often times needed to create powerful and reusable abstractions. --CostinCozianu
Maybe this "window" thing, and GUI's in general, are better served with a markup language akin to HTML. Markup seems to do "mass attributes" better than most existing imperative languages. -t
See also TooManyParameters, TooMuchGuiCode

View edit of February 20, 2014 or FindPage with title or text search