views:

98

answers:

4

I will expand here on a comment I made to http://stackoverflow.com/questions/2244860/when-a-method-has-too-many-parameters where the OP was having minor problems with someone else's function which had 97 parameters.

I am a great believer in writing maintainable code (and it is often easier to write than to read, hence Steve McConnell(praise be upon his name)'s phrase "write only code").

Since statistics how that most car accidents happen at junctions and my experience (ymmv) shows that most "anomalies" occur at interfaces, I will list some things that I do to attempt to avoid misunderstandings at interfaces and invite your comments if I am going badly wrong.

But, more importantly, I invite your suggestions for making things even more prophylactic (see, there is a question after all - how to improve things?).

  • Adequate documentation, in the form of (up to date) DoxyGen format comments describing the nature and porpoise of each parameter.
  • absolutely NO back-door shenanigans with global variables as hidden parameters.
  • try to limit parameters to six or eight. If more, pass related parameters as a structure; if they are not related then seriously reconsider the function. If it needs so much information, is it too complex to maintain? Can it be broken down into several smaller functions?
  • use the CONST as often as possible and meaningful.
  • a coding standard that says that input parameters come first, then output only, and finally input/output, which are modified by the function.
  • I also #define some empty macros to make declarations even easier to read:

    #define INPUT
    #define OUTPUT
    #define MODIFY
    bool DoSomething(INPUT int howOften, MODIFY Wdiget *myWidget, OUTPUT WidgetPtr * const nextWidget)

Just a few ideas. How can I improve on these? Thanks.

+2  A: 

I am not sure we will end at a single point of agreement about this, everyone will come up with different ideas (good or bad in each others perspective). Having said that, i find Code Complete to be a good place to go to when I am stuck with this sort of problems.

Sarfraz
+1 for Code Complete (although i am not sure where it address this topic)
Mawg
A: 

I'd use the 'rule' put forward by Uncle Bob in his book Clean Code.

These the ones I think I remember:

  • 2 parameters are ok, 3 are bad, more need refactoring
  • Comments are a sign of bad names. So there should be none, and the purpose of the function and the parameters should be clear from the names
  • make the method short. Aim for below 10 lines of code.
Jens Schauder
a function name may be clear to you but not to some random dev who will read your code years later. I think comments are always needed to make things really clear!
f4
I am Bob, and I am an uncle, and I support comments. Even with good code, the proper use of comments will make nontrivial software quicker to comprehend.
xpda
His comment on comments is based on poor comments -- ones that attempt to answer question of "what". Useful comments mostly answer question of "why". For example: "This uses insertion sort instead of quick sort because the data always starts out at least 95% sorted." They're particularly useful in cases like that, where an unusual choice is made, and a later reader *might* assume it was done out of ignorance and would otherwise waste time on mis-guided changes.
Jerry Coffin
+1  A: 

A big peeve of mine is control coupling between functions. (Control coupling is when one module controls the execution flow of another, by passing flags telling the called function what to do.)

For example (cut & paste from code I just had to work on):

void UartEnable(bool enable, int baud);

as opposed to:

void UartEnable(int baud);
void UartDisable(void);

Put another way -- parameters are for passing "data", not "control".

Dan
+4  A: 

Addressing your points in order:

  1. Well-designed types usually render Doxygen format comments a waste of time.
  2. While true as stated ("shenanigans" are bad by definition), not all use of globals is really as bad as many people imply. If you have to pass a parameter more than about four times before it's really used, chances are that a global will be less error prone.
  3. Eight or even six parameters is usually excessive. Any more than two or three starts to indicate that the function is doing more than one thing. One obvious exception is a constructor that aggregates a number of other items into an object (e.g. an address object that takes a street name, number, city, country, postal code, etc., as inputs).
  4. Better stated as "write const-correct code."
  5. Given C++'s default parameter capability, it's generally best to sort in ascending order of likelihood to use a default value.
  6. Don't. Just don't! If it's not obvious what are inputs and what are outputs, that pretty much proves that the basic design is fatally flawed.

As for ideas I think are actually good:

  1. As implied in the first point, concentrate on types. Once you get them right, most of the other problems just disappear.
  2. Use a few (even just one) central theme(s). For Lisp, everything is a list. For Unix, everything is a file (and files are all simple streams of bytes). Emulate this simplicity.

Edit: replying to comments:

  1. While you do have something of a point, my experience still indicates that documentation produced with Doxygen (and similar such as javadoc) is almost universally useless. In theory the tool doesn't prevent decent documentation, but in fact it's rare at best.
  2. Globals certainly can cause problems -- but I'm old enough to have used Fortran back before it provided much alternative, and with some care it really wasn't nearly as bad as many people imply. A lot of the stories seem to be at least third hand, with a bit of extra "spice" added each time they're re-told. I've seen one story that sounds a lot like an exaggerated version of one I told a couple decades ago or so...
  3. Hm...Markdown formatting doesn't seem to approve of my skipping numbers.
  4. And again...
  5. My comment was specific to C++, but quite a few other languages also support default parameters and/or overloading, and it can apply about as well to most of them. Even without it, a call like f(param1, param2, 0,0,0); is pretty easy to see as having default parameters. To an extent, ordering by usage is handy, but when you do the order you pick doesn't matter nearly as much as simply being consistent.
  6. True, a void * parameter doesn't tell you much -- but a MODIFY void * is little better. A real type and consistent use of const provides far more information and gets checked by the compiler. Other languages may not have/use const, but they probably don't have macros either. OTOH, some directly support what you want -- e.g., Ada has in, out and inout specifiers.
Jerry Coffin
+1 for comments on types. While everything is not necessarily a list in Lisp this is still a good point.
Andrew Myers
@Andrew: My oversimplification is merely another example of the same idea. That's my story and I'm stickin' to it!
Jerry Coffin
1 "Well-designed types usually render Doxygen format comments a waste of time" - perhaps if you are looking in the code. Perhaps not if you are looking on the company's itra-web for interface documentation (which is easier to navigate with hyperlinks).2 - globals not bad?? Hmm, ok, make that "naked" globals. If not wrapped in a fucntion, globals are A Bad Thing. YMMNV.3 - yes, I tend to smell bad code at about 4.5 - excellent point!! (although the post is language agnostic) IS it enough if I reorder to output, modify, input?contd...
Mawg
6 - it cost nothing and `void * max` is not inherently obviously either input, modify or output Although I may seem to disagree on a few points (I am a born quibbler), this is probably the best answer so far. Thanks.
Mawg