1

What considerations should I mind when designing methods or functions that take in a lot of parameters? A lot meaning over 4 but less than 10.

Example, I am debating whether to pass in an array like so:

function makeAssembly(array $params)
{
    $pump = $factory->fromModelNumber($params['modelNumber'], $params['stages'], $params['x']);        
    $motor = $factory->createMotor($params['frameId'], $params['productId'], $params['x']);
}

versus spelling out parameters in the method header:

function makeAssembly($modelNumber, $stages, $x, $frameId, $productId)
{
    $pump = $factory->fromModelNumber($modelNumber, $stages, $x);
    $motor = $factory->createMotor($frameId, $roductId, $x);
}

Is there a way that is clearly better or are both interchangeable?

In particular, are there any tenants that can be attributed to dependency injection, such as Tell Don't Ask principles that can be reused in this case as well?

4
  • Between 4 and 10 parameters? I don't think there are any special considerations. If you're writing 10-parameter methods a lot, however, I would consider reevaluating your design strategies. Jul 10, 2019 at 14:37
  • It seems that both methods only rely on $x, is there a need for the makeAssembly method?
    – Matthew
    Jul 10, 2019 at 14:39
  • @Matthew, not sure how you mean it. The method is a factory, and makeAssembly is a method to create an Assembly that hides implementation details to create a fairly complex object with lots of specs. The object has a pump that can created by using a $modelNumber or $productId (not shown). A motor object is also created and is part of the Assembly. makeAssembly thus houses a complex object creation details that I want to hide and encapsulate so that the somewhat messy legacy code won't be sprinkled in many other areas, and be contained to the makeAssembly method....
    – Dennis
    Jul 10, 2019 at 15:38
  • as far as considerations, I am thinking that using an array hides parameters, and I may need to implement extra checks to ensure the required parameter is specified, where using parameters explicitly in method header, forces the issue that they must be given, and this issue is controlled by the language interpreter. With explicit parameter specification though tricky issues may arise, such as I need either $a or $b to i.e. create an object, but not both. Using named variables for parameters forces you to specify both, function x($a, $b), where only one of those is required.
    – Dennis
    Jul 10, 2019 at 15:57

3 Answers 3

1
function makeAssembly(array $params)
{
    $pump = $factory->fromModelNumber($params['modelNumber'], $params['stages'], $params['x']);        
    $motor = $factory->createMotor($params['frameId'], $params['productId'], $params['x']);
}

The unforgivable sin here is that you force the users of this function to reverse engineer knowledge of what keys the array must hold. It's hard to even tell that it needs 5 parameters.

It doesn't help that your idea of a meaningful name for what makeAssembly() depends on is "params". Even calling it "identifiers" would be better. If that means some of the params don't fit great! Kick them out and give them their own descriptive name. A good name makes clear what does and doesn't belong. Names like "params" accept all comers. Feh! You might as well have labeled them "thingies".

function makeAssembly($modelNumber, $stages, $x, $frameId, $productId)
{
    $pump = $factory->fromModelNumber($modelNumber, $stages, $x);
    $motor = $factory->createMotor($frameId, $roductId, $x);
}

My only problem here is that it's hard to remember what order these 5 things go in. I'm trying to write code here. Stop making me work so hard.

If you can't use named arguments one way to fix this is to, again, break things up.

function makeAssembly($identifiers, $stages, $x)
{
    $pump = $factory->fromModelNumber(
        $identifiers['modelNumber'], 
        $stages, 
        $x
    );        
    $motor = $factory->createMotor(
        $identifiers['frameId'], 
        $identifiers['productId'], 
        $x
    );
}

A little white space massaging also helps. I'd try to lump stages and x together but these names give me no idea what they are. That's bad. Find out what they are and give them good names. Then try again.

1

A number of considerations:

Can you spot clusters of arguments that are related to each other, that belong together somehow, more than to the others? If so you may be missing a class. You could create an object and pass that instead of the loose arguments. This would be more meaningful. Like a rectangle instead of left, top, width and height. The rectangle object would have a name that conveys understanding.

Is your method gradually growing into a does-it-all method? This happens a lot in places where things are not thought through that well before they are implemented. Soon it will appear the method does not quite solve the problem and someone is given the task to fix it. He could start over, which would be a lot of work and quite a statement to the guy who created the thing in the first place... or he can add one little argument to fix the problem until some user finds this is not good enough either, which will likely take a while. This is not deliberate design though so perhaps not relevant to the question but a common reason for methods with a lot of arguments.

The more arguments, the more work the method will typically do. Can you split up the method's task and create separate methods instead that each have less arguments? This would simplify things a lot and reduce the chances of overlooking something.

If none of the above applies you may still consider to create some overloads, thus only forcing the programmer to deal with the full blown argument list if he really needs them all. The simpler overloads could use common defaults for the missing arguments.

0

An arbitrary array of parameters that requires certain names and values is the worst possible way to design a function that needs parameters passed in. This should only be used as a last resort. It is better to give every parameter a meaningful name even if its a long list of parameters. An even better solution is to combine related parameters in their own objects and pass those as parameters.

You are already breaking things into $pump and $motor, make the function have those as parameters. This makes your dependencies more clear, it also means makeAssembly doesn't have to check if it has a good pump and motor, it is now the responsibility of the calling code to ensure that the pump and motor are properly created. If there are complexities around creating pumps and motors let that be handled in their respective objects, that way make assembly is only focused on the logic to make an assembly.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.