Code design: introduce new method vs add optional parameter to existing method
https://softwareengineering.stackexchange.com/questions/393589
-
26-02-2021 - |
Frage
In JavaScript (ES6), I can have optional function parameter with default value. So to add a new behavior to the existing code base, I could either introduce new method(s) or extend the existing method with an optional parameter.
Consider the following example:
Existing method:
doSomething(param) {
// code to do something trivial
}
Desired new behavior: the ability to do something special after doing something trivial
Option 1: extend the existing method
doSomething(param, isSpecial = false) {
// code to do something trivial
if (isSpecial) {
// code to do some more thing special
}
}
Usage:
doSomething(param); // do something trivial
doSomething(param, true); // do something special
Option 2: add a new method
doSomethingSpecial(param) {
doSomething(param); // just call the existing method to do something trivial
// code to do some more thing special
Usage:
doSomething(param); // do something trivial
doSomethingSpecial(param); // do something special
Option 3: extend the existing method and add two new methods
doSomething(param, isSpecial) {
// code to do something trivial
if (isSpecial) {
// code to do some more thing special
}
}
doSomethingTrivial(param) {
doSomething(param, false);
}
doSomethingSpecial(param) {
doSomething(param, true);
}
Usage:
doSomethingTrivial(param); // do something trivial
doSomethingSpecial(param); // do something special
Currently I'm mostly using the first option, because it's less code to write. But thinking of the open closed principle, it doesn't sound right for me.
So which option should I choose? (in general, not special in JavaScript)
Lösung
I'd choose option 2 for the following reasons:
- A separately named method reveals the intention better than a boolean parameter that you need to look up to understand what it does.
- Since you're not modifying any existing behaviour but simply doing something extra at the end, there isn't a good reason to change the original method to take an additional parameter.
Andere Tipps
There is no right answer to this. In a general sense, you actually have three options:
- Add an optional parameter,
- Add a second function with a different name,
- Add a second function with the same name, ie overload the function.
I don't think JavaScript supports the third option, but I'm out of date with that language so may have that wrong. But many languages do, so it remains a generalised option.
I personally dislike optional parameters and actively avoid using them. I favour multiple functions with names that describe the reasons for the different parameters. And I'll use overloading when it makes sense to me to do so. But that "makes sense" is highly subjective, as are my other decisions. Other people will have different ideas on what they prefer.
The best advice for such things I've found is, be consistent. It's far more important that you define a set of rules for yourself for when to use each option and stick to them, than it is to worry about which option might be the better one.
If you follow Bob Martin's ''clean code'', then you might prefer option 2 (as in @David Arno and @casablanca.) Indeed,
- One function argument is better than two
- Simple well-named functions are better than complicated, vaguely named
- Each function should do one and only one thing (ie, single responsibility principle at the basic level)
- Open-closed principle: open for extension, closed for modification. (Usually this is defined at a class level, but can also apply at a function level.)
It's also a good practice to avoid booleans, as they are not very meaningful for the business domain.
Some cases, however, may justify other options. For instance, if you have more than two types of "special", you could define a Specials enum
and a dispatcher function:
doSomething(param, special){
//or a switch statement
if (special == Specials.trivial) {
doSomethingTrivial(param); }
if (special == Specials.bogStandard) {
doSomethingBogStandard(param); }
if (special == Specials.special) {
doSomethingTrivial(param); }
}
// code to do trivial
doSomethingTrivial(param) { ...; }
// you get the idea
doSomethingSpecial(param) {...;}
doSomethingBogStandard(param) { ...; }
Compared to, say, hardcoding doSomethingTrivial()
in a module, this gives you a flexibility to configure the module by passing the specialEnum
. (In some languages you may pass the function instead.) If a particular module is always trivial, then you could hardcode this module and leave flexibility for other modules.
As usual "it depends", and there're trade-offs to consider. And of course, it's possible to refactor later.