Optimal number of arguments to a method or function

Bryan Hogan posted a thought provoking blurb a couple weeks ago where he postulated that passing a data structure as the parameter to a method is better than passing numerous parameters. Specifically, he said: “If you have more than say two arguments that are needed to be passed into a function, use argumentCollection!” In his opinion this is better:

email = structnew();
email["subject"] = "test subject";
email["to"] = "to@test.com";
email["from"] = "from@test.com";
email["content"] = "check this out!";
email["type"] = "text";
// send the email using the SendEmail() function
SendEmail(email);

than writing this:

SendEmail("test subject", "to@test.com", "from@test.com", "check this out", "text");

His hypothesis didn’t sit right with me so I picked up my Code Complete book and went to see what Steve had to say about the subject. Chapter 5, “Characteristics of High-Quality Routines”, mentions that there are 5 levels of coupling between routines: simple-data coupling, data-structure coupling, control coupling, global-data coupling, and pathological coupling. Bryan’s code sample seemed to me to be an example of data-structure coupling, which is defined as “.. two routines are data-structure-coupled if the data passed between them is structured and is passed through a parameter list.” (it’s also called stamp coupling for anyone who cares). The example that uses multiple parameters is an example of simple-data coupling: “… two routines are simple-data-coupled if all the data passed between them is nonstructured and it’s all passed through a parameter list. This is often called ‘normal coupling’ and is the best kind.’

These definitions certainly don’t prove Bryan’s hypothesis, but they don’t disprove him either. It seems then that the real juice of his argument is that one or two parameters is better than four or five. Chapter 5, section 7 of Code Complete, titled “How to Use Routine Parameters” has this to say: “Limit the number of a routine’s parameters to about seven… If you find yourself passing more than a few arguments, the coupling amoung your routines is too tight. Design the routine or group of routines to reduce the coupling.

Stepping outside of academia for a second, I’d personally much rather use a method where either:

a) I can read the parameter list and know exactly what is required. For example:

sendEmail(string to, string from, string subject, string body, string type)

is concise and explicit. It leaves nothing for me to mess up.

or b) I can pass an object (which presumably has either defined getters or publicly accessible instance variables). For example, I might define an email package:

EmailPackage email = new EmailPackage(to, from, subject, body, type);

and then send that package to the method:

EmailDeliverer.send(email);

Conclusion? Using more than 2 parameters is definitely not a bad practice, using a data structure instead of a number of parameters is sometimes a good practice, but not the best. Thinking about you should best structure your code? Priceless. Keep up the great thoughts Bryan.

3 thoughts on “Optimal number of arguments to a method or function”

  1. Just want to make a note that this kind of reasoning *might* be OK in CF, but please, NEVER do it Bryan’s way if you’re using C++, C#, Java, or any other static type checking language. Passing the structure totally robs the compiler of the ability to verify the presence of required data (unless the structure class itself is defined in such a way that lets the compiler enforce required data). At my old job, this was the kind of code I’d see every day.

    Person p = new Person();
    p.setFirst(“Joe”);
    p.setLast(“Cheng”);
    p.setAddress(“…”);

    p.setEmail(“…”);
    personFactory.registerNewUser(p);

    Ugh, it makes my skin crawl just writing that.

    What if we decide later that country is a required parameter? The compiler can’t help us verify this. This would’ve been better:

    // required params are part of constructor
    Person p = new Person(“Joe”, “Cheng”, … Countries.US);
    p.setAge(25); // optional values are set via accessors
    personFactory.registerNewUser(p);

    Or really, why fight it:

    // data structure for optional params only
    RegParams optional = new RegParams();
    optional.setAge(25);
    personFactory.registerNewUser(
        “Joe”,
        “Cheng”,
        …
        Countries.US,
        optional);

    IMO there is nothing inherently wrong with passing lots of parameters (if people reading your code are using IDE’s); it’s just often an indication that you’re passing information that is incidental to the semantics of what you’re actually doing (i.e., you’re passing implementation details, so there’s something wrong with the overall design of the classes/interfaces). In the rare cases where, semantically, the caller and callee inherently have a lot of values to exchange, I think it’s fine to pass them all individually as parameters.

  2. AJ, this line:

    “Bryan’s code sample seemed to me to be an example of data-structure coupling, which is defined as “.. two routines are data-structure-coupled if the data passed between them is structured and is passed through a parameter list.”

    Is this still true if the use of argumentCollection ‘expands’ out? In CF, using foo(argumentCOllection=f) is NOT the same as doing foo(somestruct). ArgumentCollection acts as if you had actually done foo(x=y,a=b) etc.

Leave a Reply to Joe Cheng Cancel reply

Your email address will not be published. Required fields are marked *