Avoid copying switch statments

Viewed 75

Can this code be refactored to avoid copying switch statements?

enum class Animal
{ Cat, Dog, Fish};

float GetMaxSpeed(Animal a)
{
 switch (a)
    {
        case Animal::Cat:
            return 30;
        case Animal::Dog:
            return 40;
        case Animal::Fish:
            return 15;
    }
 }

string GetGermanTranslation(Animal a)
{
 switch (a)
    {
        case Animal::Cat:
            return "Katze";
        case Animal::Dog:
            return "Hund";
        case Animal::Fish:
            return "Fische";
    }
 }

Obviously, this is a toy example, my real enum class is much larger.

2 Answers

A more object-oriented approach would consist of relying on polymorphism instead of continuously switching on an enumerator value representing a type.

To follow this approach, first, define an abstract class, Animal, that specifies the member functions its subclasses should implement:

class Animal {
public:
    virtual std::string GetGermanTranslation() const = 0;
    virtual float GetMaxSpeed() const = 0;
    virtual ~Animal() = default;
};

Then, publicly derive from this class and override the virtual member functions GetGermanTranslation() and GetMaxSpeed().

For example, for Cat:

class Cat: public Animal {
public:
   std::string GetGermanTranslation() const override { return "Katze"; };
   float GetMaxSpeed() const override  { return 30; };
};

Then, similarly for Dog:

class Dog: public Animal {
public:
   std::string GetGermanTranslation() const override { return "Hund"; };
   float GetMaxSpeed() const override  { return 40; };
};

You can analogously define the Fish class.

Finally, having a pointer or reference to an Animal object, you just call the corresponding virtual member functions:

void displayAnimal(const Animal& animal) {
   std::cout << "The " << animal.GetGermanTranslation();
   std::cout << " runs at " << animal.GetMaxSpeed() << '\n';
}

As you can see, there is no more switching on an enumerator that represents a type for distinguishing the different animals.

You can call this function, displayAnimal(), with different Animal objects:

auto cat = std::make_unique<Cat>();
displayAnimal(*cat);

auto dog = std::make_unique<Dog>();
displayAnimal(*dog);

If you don't specify otherwise, the first enumerator in an enumerated type has the value 0, and subsequent enumerators have a value that's one more than their predecessor. So with

enum Animal
{ Cat, Dog, Fish};

the value of Cat is 0, the value of Dog is 1, and the value of Fish is 2. To map those values to some other set of values, just use them as an array index:

int max_speed[] = {
    30, 40, 15
};

const char* german_name[] = {
    "Katze",
    "Hund",
    "Fische"
};

Now you can write things like max_speed[Cat] and german_name[Dog].

Note that the string literals are inside double quotes, e.g., "Katze", not single quotes as in the question. Single quotes around multiple characters create a funky thing called a "multicharacter literal". They're pretty much useless.

Related