5

According to Is it wrong to use a boolean parameter to determine behavior?, I should not use a boolean parameter to switch behavior inside a function. How about other types of parameters?

For example, if I try to use a string instead of boolean:

void f(str){
    if(str==='a'){
        a();
    }else if(str==='b'){
        b();
    }else{
        c();
    }
}

does it also suffer from the problems same as boolean parameters? Is it true that we should split methods with several behaviours into multiple methods with one behaviour only, regardless of the type of parameter (e.g.:boolean, string, enum...)?

  • 2
    Read the top answer to your linked question, it applies to this question just as well. – Paul Feb 27 '18 at 2:41
6

I like polymorphism. I recommend it to all my friends. A polymorphic solution would look like this:

f(thingy) {
    thingy.doIt()
}

class A { doIt() { a(); } }

class B { doIt() { b(); } }

class C { doIt() { c(); } }

thingyFactory(str){
    if(str==='a'){
        return A();
    }else if(str==='b'){
        return B();
    }else{
        return C();
    }
}

f(thingyFactory("a"))
f(thingyFactory("b"))
f(thingyFactory("c"))

Er wait, it looks like you're using javascript. Here's some functional polymorphism.

a = function (){ document.writeln( "<br> a" ); }
b = function (){ document.writeln( "<br> b" ); }
c = function (){ document.writeln( "<br> c" ); }


factory = function(str) {
   if(str==='a'){
        return a;
    }else if(str==='b'){
        return b;
    }else{
        return c;
    }
}

polymorphic = factory('b');
polymorphic();

As you can see we end up using a parameter anyway. It's not so much about never using parameters to make decisions. It's about not coupling making those decisions with doing what's been decided. Sometimes making those decisions is expensive, Sometimes we can make them at compile time. sometimes it's just hard to look at, sometimes you want different areas of code to handle these two separate responsibilities.

And of course sometimes OO purists over engineer. Never do stuff like this without understanding why you're doing stuff like this. Doing this gets you something. Be sure you benefit from that something. Otherwise all you're getting is more code.

0

I like @CandiedOrange's answer, but for completeness-sake, there are other ways you can accomplish this, too. You could use a map (sometimes called a hash or dictionary) to map inputs to functions to call. You don't specify a language, but in C++, it would look something like this:

#include <iostream>
#include <map>

typedef void (*actionFunction)();

void functionA()
{
    std::cout << "a\n";
}

void functionB()
{
    std::cout << "b\n";
}

int main(int argc, char *argv[])
{
    std::map<char, actionFunction>  functionMap; // <- this is the map of characters to functions
    functionMap [ 'a' ] = functionA;
    functionMap [ 'b' ] = functionB;

    functionMap [ 'a' ](); // <- Calls the function associated with 'a'

    return 0;
}

You can do a similar thing in straight C with an array of records that contain the mapping above:

#include <stdio.h>

typedef void (*actionFunction)();

typedef struct functionMap {
    char            index;
    actionFunction  func;
} functionMap;

void functionA()
{
    printf("a\n");
}

void functionB()
{
    printf("b\n");
}

int main()
{
    functionMap fMap[] = {  // <- here's the map
        { 'a', functionA },
        { 'b', functionB }
    };
    size_t  numMaps = sizeof(fMap) / sizeof(fMap [ 0 ]);
    char someInput = 'a';

    for (size_t i = 0; i < numMaps; ++i)
    {
        if (fMap [ i ].index == someInput)
        {
            fMap [ i ].func(); // <- this calls the function associated with "someInput"
            break;
        }
    }

    return 0;
}

These fall into the broader category of table-driven or data-driven programming.

  • I've seen this before but I never really understood what's really to be gained with it? is it any better that plain old polymorphism?? – Zalomon Feb 27 '18 at 11:39
  • 1
    You are essentially looking up a function and executing it. It doesn't require new objects. Those functions can be methods within the object, global functions, or methods on another object altogether. It adds a lot of flexibility, but at the cost of some complexity – Berin Loritsch Feb 27 '18 at 13:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.