If you have been working as a software developer, you are probably aware that following Uncle Bob’s advises regarding number of arguments a function should take is difficult and requires some experience.
Clean Code states among others that:
The ideal number of arguments for a function is
Clean Code
zero (niladic). Next comes one (monadic), followed
closely by two (dyadic). Three arguments (triadic)
should be avoided where possible. More than three
(polyadic) requires very special justification—and
then shouldn’t be used anyway.
I like this quote because it provides rules that are easy to understand, but I believe the whole chapter slightly misses the point.
Forget about number of arguments, keep single level of abstraction
Most functions with plenty of arguments suffer from many levels of abstraction within a single function.
public class GamesFactory { public Game GameFrom( IStadium stadium, ITeam homeTeam, DateTime dateTime, IReferee referee, ITeam awayTeam, int audienceCount ) { } }
Clearly there are too many arguments in this function – among high-level objects there’s some low-level data as well and it exposes a couple of problems:
- Point in time is a low-level idea in this context, what if a game will be postponed and we’d like to have previous and current version?
- The only representation of audience is a sheer number of fans participating – this is primitive obsession and will require refactoring this high-level code because of low-level problems – what about count of VIPs for example?
- Although the code is designed to handle interaction of very meaningful objects, there are also details the developer has to care about when visiting this code.
A solution to this is keeping a single level of abstraction within a single function. You should not put a high-level objects into a sorting algorithm and, like in this case, you should not put low-level data into a very general function.
What can I do about it?
Merge. Merge until you have a single level of abstraction that not only suites the responsibility of the function but also is easy to reason about.
public class GamesFactory { public Game GameFrom( ICircumstances circumstances, //stadium+time+audience ITeam homeTeam, ITeam awayTeam, IReferee referee ) { } }
In order to have a football game, we need a place, two teams and a referee. The code corresponds with the reality and there’s reason to further reduce number of arguments.
The conclusion is that if you want a reasonable number of arguments in function, you should focus on single level of abstraction.