Sunday, December 18, 2011

args, opts, params

It's quite popular, especially in Ruby code, to see methods like this (note the method arguments):
def show(args)
  some_other_object(args)
end
or
def show(opts)
  .. lots of code
  bar = opts[:foo]
  opts[:bar]
end
and also:
def show(name, params)
  some_other_object(name, params[:foo])
end
I see some problems with such code:

1. It's hard to tell, when you call the method, what is really required/expected to pass in.

It's less of a problem, when you can see in the method body what is used. But it's not always the case. Often the params are passed further, so you have to look around and think hard how the params are used.


2. (args, opts, params) as names sound very generic, they don't tell me too much.

I understand that hashes are quire useful sometimes. But it's a similar situation like the one where we would call a variable a var_1.
Why don't we, at least, give them good names, like address_hash or html_options or maybe game_configuration?

3. It's a missing object smell.

Very often, to me, the examples above are code smells. There's a missing object somewhere. Especially when this data is passed further. Maybe it's Address object, maybe PopupContent, whatever sounds good in your domain.

What's your opinion?

3 comments:

koszcz said...

I agree so much! Hashes are really great tool unless they are used as method parameters.
I always try not to do that by defining methods with multiple "simple type" parameters, but...
Do you know active_doc library? (https://github.com/iNecas/active_doc) in shortcut it allows you to define more info about method parameters (even gives some kind of error or exception, i dont remember which one, if parameter does not goes along with definition)
For example if you have method def do_sth(param)...
You can define that param must be hash which must have specific keys and value of each of them must have specific type. What do you think about it?

Anonymous said...

Definitely, it's a smell. But it's a style that has been promoted (sanctified, even) by our favorite framework :)

On the other hand, if it's really an "opts-style" parameter, what alternative would be better? A 20 boolean parameters? A new class every time?

Gernot said...

I agree that method signatures should be as simple as possible. But then there are those methods where multiple, optional arguments make perfect sense.

Since Ruby does not support named parameters (yet), the best way I know to mimic named parameters is to use a hash. A call to assert_valid_keys makes sure no caller passed an unexpected option and is some sort of lightwheight documentation. We then document the avaliable options in a comment block above the method.

I really like the expressiveness of method calls with an options hash.