Stated the main two reasons why functions with lots of parameters exist:
Because they're missing an abstraction, or because the function is
trying to do more than one job.
Added explicit coverage of those reasons and another example
illustrating that.
This commit is contained in:
hsutter 2017-03-20 11:35:29 -07:00
parent b8b178e98a
commit 38a6acc1c0

View File

@ -1864,15 +1864,25 @@ It is usually best to avoid global (namespace scope) objects altogether.
Having many arguments opens opportunities for confusion. Passing lots of arguments is often costly compared to alternatives. Having many arguments opens opportunities for confusion. Passing lots of arguments is often costly compared to alternatives.
##### Discussion
The two most common reasons why functions have too many parameters are:
1. *Missing an abstraction.* There is an abstraction missing, so that a compound value is being passed as individual elements instead of as a single object that enforces an invariant. This not only expands the parameter list, but it leads to errors because the component values are no longer protected by an enforced invariant.
2. *Violating "one function, one responsibility."* The function is trying to do more than one job and should probably be refactored.
##### Example ##### Example
The standard-library `merge()` is at the limit of what we can comfortably handle The standard-library `merge()` is at the limit of what we can comfortably handle:
template<class InputIterator1, class InputIterator2, class OutputIterator, class Compare> template<class InputIterator1, class InputIterator2, class OutputIterator, class Compare>
OutputIterator merge(InputIterator1 first1, InputIterator1 last1, OutputIterator merge(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2, InputIterator2 first2, InputIterator2 last2,
OutputIterator result, Compare comp); OutputIterator result, Compare comp);
Note that this is because of problem 1 above -- missing abstraction. Instead of passing a range (abstraction), STL passed iterator pairs (unencapsulated component values).
Here, we have four template arguments and six function arguments. Here, we have four template arguments and six function arguments.
To simplify the most frequent and simplest uses, the comparison argument can be defaulted to `<`: To simplify the most frequent and simplest uses, the comparison argument can be defaulted to `<`:
@ -1894,12 +1904,24 @@ Alternatively, we could use concepts (as defined by the ISO TS) to define the no
Mergeable{In1 In2, Out} Mergeable{In1 In2, Out}
OutputIterator merge(In1 r1, In2 r2, Out result); OutputIterator merge(In1 r1, In2 r2, Out result);
##### Example
The safety Profiles recommend replacing
void f(int* some_ints, int some_ints_length); // BAD: C style, unsafe
with
void f(gsl::span<int> some_ints); // GOOD: safe, bounds-checked
Here, using an abstraction has safety and robustness benefits, and naturally also reduces the number of parameters.
##### Note ##### Note
How many arguments are too many? Try to use less than Four arguments. How many parameters are too many? Try to use fewer than four (4) parameters.
There are functions that are best expressed with four individual arguments, but not many. There are functions that are best expressed with four individual parameters, but not many.
**Alternative**: Group arguments into meaningful objects and pass the objects (by value or by reference). **Alternative**: Use better abstraction: Group arguments into meaningful objects and pass the objects (by value or by reference).
**Alternative**: Use default arguments or overloads to allow the most common forms of calls to be done with fewer arguments. **Alternative**: Use default arguments or overloads to allow the most common forms of calls to be done with fewer arguments.