Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is replacing conditionals with polymorphism useful?

There's one thing that has always stumped me about "replacing a conditional with polymorphism" as shown on refactoring.guru which I would like to clarify. I have re-created the example in python below:

IF STATEMENT CODE

def get_speed(bird):
    base_speed = 10

    if bird == "AfricanBird":
        return base_speed * 2 - 10
    elif bird=="EuropeanBird":
        return base_speed * 1.85 - 12
    elif bird=="NorwegianBlue":
        return base_speed * 2.5 - 11
    else:
        raise ValueError("Bird type invalid")

if __name__=="__main__":
    b = "EuropeanBird"
    speed = get_speed(b)
    print(speed)

REPLACING IF STATEMENT WITH POLYMORPHISM

from abc import abstractmethod, ABC

class Bird(ABC):
    def get_base_speed(self):
        return 10

    @abstractmethod
    def get_speed(self):
        pass

class AfricanBird(Bird):
    def get_speed(self):
        return self.get_base_speed()*2-10

class EuropeanBird(Bird):
    def get_speed(self):
        return self.get_base_speed()*1.85-12

class NorwegianBlue(Bird):
    def get_speed(self):
        return self.get_base_speed()*2.5-11

if __name__=="__main__":
    # This *still* has to be specified by the user somewhere.
    # For example in a GUI or input text file.
    b = "EuropeanBird"

    # Here is the if statement again!!
    if b == "AfricanBird":
        b = AfricanBird()
    elif b=="EuropeanBird":
        b = EuropeanBird()
    elif b=="NorwegianBlue":
        b = NorwegianBlue()
    else:
        raise ValueError("Bird type invalid")

    print(b.get_speed())

I get the part where polymorphism adds generality by allowing you to inherit from a base class and redefine the behavior instead of adding a new branch in the if statement. However, at the end of the day you still need to make a decision somewhere in your main code for which implementation of your base class to use (line 26 in my second example), which forces the if statement to reappear.

Am I missing the point? Is there any way of completely eliminating the conditional statement in the client code?

like image 535
user32882 Avatar asked Feb 27 '26 18:02

user32882


2 Answers

In the IF STATEMENT variant, the cases must be handled alongside the computation. In the POLYMORPHISM variant, the cases can be handled as early as receiving the input, but could also be pushed arbitrarily further along. Using Polymorphism, the case decision and result are decoupled; one could even re-use the same decision to derive multiple results.

In short, both require to make the decision somewhere but Polymorphism allows choosing this somewhere.


While logically, something has to conditionally switch between the cases, this can be done without explicitly enumerating if branches.

The simplest means is to have a table from identifier to type. The base class can encapsulate this:

class Bird(ABC):
    _birdies = {}
    _base_speed = 10

    @classmethod
    def breed(cls, kind: str):
        """Create a new bird of a given kind"""
        return cls._birdies[kind]()

    # register subclasses for lookup on definition
    def __init_subclass__(cls):
        Bird._birdies[cls.__name__] = cls

    @property
    @abstractmethod
    def speed(self):
        raise NotImplementedError

class AfricanBird(Bird):
    @property
    def speed(self):
        return self._base_speed * 2 - 10

class EuropeanBird(Bird):
    @property
    def speed(self):
        return self._base_speed * 1.85 - 12

Bird.breed("AfricanBird")  # <__main__.AfricanBird at 0x1106d1fa0>

Note that the class is only used as a frontend to hide the table. There is no polymorphism used here other than assuming that all subclasses can be interchangeably constructed without arguments.

like image 109
MisterMiyagi Avatar answered Mar 02 '26 06:03

MisterMiyagi


In your example, you are converting a mere string to a full hierarchy of classes. As all what if required is handling a string, and as the input is that string, the polymorphism adds nothing except learning.

Things are different when various objects are created internally, and when those objects have some common behaviour with specificities. In fact the choice should arise at conception time, before writing any code line. You analyse what objects are to be processed, and what behaviour they will have to implement. At that point, the possible hierarchies of classes should arise.

In fact, replacing a bunch of conditional with polymorphism should only occur if you have skipped the modeling phase in development, and find yourself consistently repeating the same conditional throughout your code. I have been using Python, Java and C++ for decades, and have never replaced conditionals with polymorphism: either the model exhibits a hierarchy at conception time, and it has to be implemented first, or there are no underlying objects and polymorphism will only add an useless complexity.

TL/DR: if you find yourself replacing conditionals with polymorphism, it just mean that you have written code without first thinking about what you wanted to develop and how you should to it.

like image 22
Serge Ballesta Avatar answered Mar 02 '26 06:03

Serge Ballesta