面向接口编程与依赖倒置
在面向对象设计里,我们常说'对接口编程,而非具体实现'。这话听着耳熟,但落实到 Python 里怎么操作?咱们拿个经典的'人类吃汉堡'场景来切入。
硬契约:抽象基类(ABC)
传统的做法是定义一个抽象基类,让具体实现去继承它。这种方式属于'硬契约',强制要求子类在'族谱'上必须属于某个基类。
from abc import ABC, abstractmethod
class Burger(ABC):
@abstractmethod
def eat(self):
pass
class ChickenBurger(Burger):
def eat(self):
print("啃鸡腿堡")
class Human:
def eat_lunch(self, burger: Burger):
burger.eat()
# 调用示例
Human().eat_lunch(ChickenBurger())
通过上面的代码可以看到,Human的 eat_lunch方法只是依赖了 Burger 接口,而不是依赖了具体的 ChickenBurger。后续如果想换别的汉堡,比如牛肉堡,只要继承 Burger 并实现 eat 方法即可,Human 类的代码一行都不用改。这就是依赖倒置原则的体现。
类型检查的陷阱
请注意,在 Python 中,类型注解并不会强制检查,而只是一种静态提示。Python 解释器不会去理会参数的类型注解。
# 即使传入的不是显式继承 Burger 基类的实例,也可以成功运行
class Dumpling:
def eat(self):
print("吃饺子")
Human().eat_lunch(Dumpling()) # 运行时不会报错
因为类型注解只是静态检查。如果我们想要运行时检查,就得使用 isinstance() 显式检查。不过,这样做其实也可以通过注册虚拟子类绕过去。
Burger.register(Dumpling)
((Dumpling(), Burger))


