Files
codeql/python/ql/src/Metrics/NumberOfParametersWithoutDefault.qhelp
2018-11-19 15:10:42 +00:00

93 lines
3.5 KiB
XML

<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>
<overview>
<p>
A function (or method) that uses a high number of parameters makes maintenance more difficult:
</p>
<ul>
<li>It is difficult to write a call to the function, because the programmer must know how to
supply an appropriate value for each parameter.</li>
<li>It is <em>externally</em> difficult to understand, because calls
to the function are longer than a single line of code.</li>
<li>It can be <em>internally</em> difficult to understand, because it
has so many dependencies.</li>
</ul>
</overview>
<recommendation>
<p>
Restrict the number of parameters for a function, according to the reason for the high number:
</p>
<ul>
<li>Several of the parameters are logically related, but are
passed into the function separately. The parameters that are logically related should be grouped together
(see the 'Introduce Parameter Object' refactoring on pp. 238-242 of [Fowler]).</li>
<li>The function has too many responsibilities. It should be broken into multiple functions (see the
'Extract Method' refactoring on pp. 89-95 of [Fowler]), and each new function should be passed
a subset of the original parameters.</li>
<li>The function has redundant parameters that are not used. The two main reasons for this are:
(1) parameters were added for future extensibility but are never used; (2) the body of the function was changed
so that it no longer uses certain parameters, but the function signature was not
correspondingly updated. In both cases, the theoretically correct solution is to delete the unused
parameters (see the 'Remove Parameter' refactoring on pp. 223-225 of [Fowler]), although you must do
this cautiously if the function is part of a published interface.</li>
</ul>
<p>When a function is part of a published interface, one possible solution is to add a new, wrapper
function to the interface that has a tidier signature. Alternatively, you can publish a new version of
the interface that has a better design. Clearly, however, neither of these solutions is ideal,
so you should take care to design interfaces the right way from the start.</p>
<p>The practice of adding parameters for future extensibility is especially
bad. It is confusing to other programmers, who are uncertain what values they should pass
in for these unnecessary parameters, and it adds unused code that is potentially difficult to remove
later.</p>
</recommendation>
<section title="Examples">
<p>In the following example, although the parameters are logically related, they are passed into the
<code>print_annotation</code> function separately.</p>
<sample src="NumberOfParameters1.py" />
<p>In the following modified example, the <code>print_annotation</code> function is simplified by logically grouping
the related parameters into a single class.
An instance of the class can then be passed into the function instead, as shown below.
</p>
<sample src="NumberOfParameters1Good.py" />
<p>In the following example, the <code>print_membership</code> function has too many responsibilities,
and so needs to be passed four arguments.</p>
<sample src="NumberOfParameters2.py" />
<p>In the following modified example, <code>print_membership</code> has been broken into four functions.
(For brevity, only one function is shown.) As a result, each new function needs to be passed only one
of the original four arguments.</p>
<sample src="NumberOfParameters2Good.py" />
</section>
<references>
<li>
M. Fowler, <em>Refactoring</em>. Addison-Wesley, 1999.
</li>
</references>
</qhelp>