問題描述
為什麼用多態性替換條件有用? (Why is replacing conditionals with polymorphism useful?)
關於“用多態性替換條件”這件事一直困擾著我。如 refactoring.guru 我想澄清一下。我在下面的 python 中重新創建了示例:
IF 語句代碼
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)
用多態替換 IF 語句
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())
我得到了多態性增加通用性的部分,它允許您從基類繼承並重新定義行為,而不是在 if
語句中添加新分支。然而,歸根結底,您仍然需要在主代碼中的某處決定要使用基類的 which 實現(我的第二個示例中的第 26 行),這會強制 if
語句重新出現。
我錯過了重點嗎?有什麼辦法可以徹底消除客戶端代碼中的條件語句?
參考解法
方法 1:
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.
方法 2:
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.
(by user32882、MisterMiyagi、Serge Ballesta)