面向接口编程与依赖倒置
'对接口编程,而不是对实现编程'这句话在 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 只依赖 Burger,不关心传进来的是鸡腿堡还是牛肉堡。后面你要换实现,只要新类继承 Burger,并把 eat 补上,Human 不用动。这就是依赖倒置真正省事的地方。
不过,ABC 也有它的毛病:它要求'出身'。有时你只是想让对象具备某个能力,不一定非得让它继承一棵家谱树。
类型注解不会替你拦住错误
Python 的类型注解更像提示,不是运行时的硬限制。解释器不会因为参数写了 Burger,就强制你传一个 Burger 的实例。
# 即使传入的不是显式继承 Burger 基类的实例,也可以成功运行
class Dumpling:
def eat(self):
print("吃饺子")
Human().eat_lunch(Dumpling()) # 运行时不会报错
这段代码能跑通,因为 Dumpling 也实现了 。如果你真要让 也认它,有两种路:要么显式继承,要么注册虚拟子类。


