Python 中的依赖倒置
Dependency Inversion in Python
我已经开始将 SOLID principles 应用到我的项目中。所有这些对我来说都很清楚,除了依赖倒置,因为在 Python 中我们没有改变在另一个 class 中定义一些 class 类型的变量(或者也许我不知道)。所以我以两种形式实现了依赖倒置原则,想知道它们中的哪一个是正确的,我该如何纠正它们。这是我的代码:
d1.py
:
class IFood:
def bake(self, isTendir: bool): pass
class Production:
def __init__(self):
self.food = IFood()
def produce(self):
self.food.bake(True)
class Bread(IFood):
def bake(self, isTendir:bool):
print("Bread was baked")
d2.py
:
from abc import ABC, abstractmethod
class Food(ABC):
@abstractmethod
def bake(self, isTendir): pass
class Production():
def __init__(self):
self.bread = Bread()
def produce(self):
self.bread.bake(True)
class Bread(Food):
def bake(self, isTendir:bool):
print("Bread was baked")
# define a common interface any food should have and implement
class IFood:
def bake(self): pass
def eat(self): pass
class Bread(IFood):
def bake(self):
print("Bread was baked")
def eat(self):
print("Bread was eaten")
class Pastry(IFood):
def bake(self):
print("Pastry was baked")
def eat(self):
print("Pastry was eaten")
class Production:
def __init__(self, food): # food now is any concrete implementation of IFood
self.food = food # this is also dependnecy injection, as it is a parameter not hardcoded
def produce(self):
self.food.bake() # uses only the common interface
def consume(self):
self.food.eat() # uses only the common interface
使用它:
ProduceBread = Production(Bread())
ProducePastry = Production(Pastry())
原理
Robert C. Martin 对依赖倒置原则的定义
由两部分组成:
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend on details. Details should depend on abstractions.
澄清一下...模块 可以是函数、class、文件...一段代码。
错误
假设您有一个程序需要您烤面包。
在更高的层次上,有一点你可以调用 cook()
一个不好的实现方式是创建一个既能做饭又能做面包的函数。
def cook():
bread = Bread()
bread.bake()
cook()
这样不好...
如您所见,cook
函数 依赖于 Bread
.
那么如果你想烤饼干怎么办?
一个菜鸟的错误就是添加这样一个字符串参数:
def cook(food: str):
if food == "bread":
bread = Bread()
bread.bake()
if food == "cookies":
cookies = Cookies()
cookies.bake()
cook("cookies")
这显然是错误的。因为通过添加更多食物,您会更改代码,并且您的代码会因许多 if 语句而变得一团糟。它几乎打破了所有原则。
解决方案
所以你需要 cook
功能,它是一个更高级别的模块,而不是依赖于较低级别的模块,如 Bread
或 Cookies
所以我们唯一需要的就是可以烘焙的东西。我们会烤它。现在正确的方法是 实现接口 。在 Python 中没有必要,但强烈建议保持代码清洁和面向未来!
If it looks like a duck, swims like a duck, and quacks like a duck,
then it probably is a duck.
他们说。
现在让我们反转依赖关系!
from abc import ABC, abstractmethod
class Bakable(ABC):
@abstractmethod
def bake(self):
pass
def cook(bakable:Bakable):
bakable.bake()
现在 cook
函数 依赖于 抽象 。不在面包上,也不在饼干上,而是在抽象上。 Any any any Bakable
现在可以烘焙了。
通过实现接口,我们确信每个Bakable
都会有一个bake()
方法来做某事。
但是现在cook
函数不需要知道了。
cook 函数将烘烤任何 Bakable
.
依赖关系现在转到客户端。客户是想要烤东西的人。客户端是一段将要使用 cook
函数的代码。客户知道要烤什么。
现在通过查看 cook
函数,客户端知道 cook
函数等待接收一个 Bakable
并且只有一个 Bakable
.
那我们来做点面包吧。
class Bread(Bakable):
def bake():
print('Smells like bread')
现在让我们创建一些 cookie!
class Cookies(Bakable):
def bake():
print('Cookie smell all over the place')
好的!现在我们来煮它们。
cookies = Cookies()
bread = Bread()
cook(cookies)
cook(bread)
我已经开始将 SOLID principles 应用到我的项目中。所有这些对我来说都很清楚,除了依赖倒置,因为在 Python 中我们没有改变在另一个 class 中定义一些 class 类型的变量(或者也许我不知道)。所以我以两种形式实现了依赖倒置原则,想知道它们中的哪一个是正确的,我该如何纠正它们。这是我的代码:
d1.py
:
class IFood:
def bake(self, isTendir: bool): pass
class Production:
def __init__(self):
self.food = IFood()
def produce(self):
self.food.bake(True)
class Bread(IFood):
def bake(self, isTendir:bool):
print("Bread was baked")
d2.py
:
from abc import ABC, abstractmethod
class Food(ABC):
@abstractmethod
def bake(self, isTendir): pass
class Production():
def __init__(self):
self.bread = Bread()
def produce(self):
self.bread.bake(True)
class Bread(Food):
def bake(self, isTendir:bool):
print("Bread was baked")
# define a common interface any food should have and implement
class IFood:
def bake(self): pass
def eat(self): pass
class Bread(IFood):
def bake(self):
print("Bread was baked")
def eat(self):
print("Bread was eaten")
class Pastry(IFood):
def bake(self):
print("Pastry was baked")
def eat(self):
print("Pastry was eaten")
class Production:
def __init__(self, food): # food now is any concrete implementation of IFood
self.food = food # this is also dependnecy injection, as it is a parameter not hardcoded
def produce(self):
self.food.bake() # uses only the common interface
def consume(self):
self.food.eat() # uses only the common interface
使用它:
ProduceBread = Production(Bread())
ProducePastry = Production(Pastry())
原理
Robert C. Martin 对依赖倒置原则的定义 由两部分组成:
- High-level modules should not depend on low-level modules. Both should depend on abstractions.
- Abstractions should not depend on details. Details should depend on abstractions.
澄清一下...模块 可以是函数、class、文件...一段代码。
错误
假设您有一个程序需要您烤面包。
在更高的层次上,有一点你可以调用 cook()
一个不好的实现方式是创建一个既能做饭又能做面包的函数。
def cook():
bread = Bread()
bread.bake()
cook()
这样不好...
如您所见,cook
函数 依赖于 Bread
.
那么如果你想烤饼干怎么办?
一个菜鸟的错误就是添加这样一个字符串参数:
def cook(food: str):
if food == "bread":
bread = Bread()
bread.bake()
if food == "cookies":
cookies = Cookies()
cookies.bake()
cook("cookies")
这显然是错误的。因为通过添加更多食物,您会更改代码,并且您的代码会因许多 if 语句而变得一团糟。它几乎打破了所有原则。
解决方案
所以你需要 cook
功能,它是一个更高级别的模块,而不是依赖于较低级别的模块,如 Bread
或 Cookies
所以我们唯一需要的就是可以烘焙的东西。我们会烤它。现在正确的方法是 实现接口 。在 Python 中没有必要,但强烈建议保持代码清洁和面向未来!
If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.
他们说。
现在让我们反转依赖关系!
from abc import ABC, abstractmethod
class Bakable(ABC):
@abstractmethod
def bake(self):
pass
def cook(bakable:Bakable):
bakable.bake()
现在 cook
函数 依赖于 抽象 。不在面包上,也不在饼干上,而是在抽象上。 Any any any Bakable
现在可以烘焙了。
通过实现接口,我们确信每个Bakable
都会有一个bake()
方法来做某事。
但是现在cook
函数不需要知道了。
cook 函数将烘烤任何 Bakable
.
依赖关系现在转到客户端。客户是想要烤东西的人。客户端是一段将要使用 cook
函数的代码。客户知道要烤什么。
现在通过查看 cook
函数,客户端知道 cook
函数等待接收一个 Bakable
并且只有一个 Bakable
.
那我们来做点面包吧。
class Bread(Bakable):
def bake():
print('Smells like bread')
现在让我们创建一些 cookie!
class Cookies(Bakable):
def bake():
print('Cookie smell all over the place')
好的!现在我们来煮它们。
cookies = Cookies()
bread = Bread()
cook(cookies)
cook(bread)