Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can polymorphism be used to replace switch or else-if statements?

I am slightly confused with what I read in the post: case-vs-if-else-if-which-is-more-efficient

It is suggested many times that long case/if-else statements should be replaced with the use of polymorphism. I am trying to get my head around what that really means. How can you replace:

case TASK_A:
    // do things for task A
    break;
case TASK_B:
    // do things for task B
    break;
        :
        :
case TASK_J:
    // do things for task J
    break;

With polymorphism? I could sort-of understand it if the "do ..." part is basically the same repetition, but if there is significant differences between some or all of the "cases" then does this still apply?

like image 985
code_fodder Avatar asked Dec 08 '22 12:12

code_fodder


2 Answers

In the example you link to, the switch is over the type of an object, and the suggestion is to use polymorphism to remove the need to check the type. That is, declare a virtual function in the base class, override it for each concrete class to do whatever needs doing, and replace the entire switch with a call to that function.

In your case, you're testing the value of a variable, not the type of an object. However, you could transform it to a polymorphic solution if you wanted:

struct Task         {virtual void do_things() = 0;};
struct TaskA : Task {virtual void do_things() {/*do things for task A*/}};
struct TaskB : Task {virtual void do_things() {/*do things for task B*/}};
//...
struct TaskJ : Task {virtual void do_things() {/*do things for task J*/}};

Then you could replace the variable you're switching over with a (smart) pointer to Task; and the switch with task->do_things(). Whether that's better than a switch is a matter of taste.

like image 122
Mike Seymour Avatar answered Dec 11 '22 01:12

Mike Seymour


You create a parent class/interface, e.g. task which defines a function (potentially abstract) that child classes override; let's call this function handle_task

You then create a child class for each type of task (i.e. each case statement above) and put the // do things for task X in that classes' implementation of handle_task

Due to polymorphism, each of these child classes can be passed around as / treated as instances of the parent class, and when you call handle_task on them the correct code will be executed.

A quick worked example:

#include <iostream>

class Task {
  public:
    virtual void handle_task()
    {
      std::cout << "Parent task" << std::endl;
    }
};

class Task_A: public Task {
  public:
    void handle_task()
    {
      std::cout << "task a" << std::endl;
    }
};

class Task_B: public Task {
  public:
    void handle_task()
    {
      std::cout << "task b" << std::endl;
    }
};

int main( void )
{
  Task *task;
  Task_A a;
  Task_B b;

  task=&a;
  task->handle_task();
  task=&b;
  task->handle_task();

}

Will print

/tmp$ g++ test.cpp
/tmp$ ./a.out
task a
task b
like image 29
Oliver Matthews Avatar answered Dec 11 '22 00:12

Oliver Matthews