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)

War es hilfreich?

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:

  1. Add an optional parameter,
  2. Add a second function with a different name,
  3. 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.

Lizenziert unter: CC-BY-SA mit Zuschreibung
scroll top