我已经开始将SOLID principles应用到我的项目中。除了依赖倒置,所有这些对我来说都很清楚,因为在Python中,我们没有改变在另一个类中定义某个类的类型变量(或者我不知道)。所以我以两种形式实现了依赖倒置原则,并想知道哪种是正确的,我如何纠正它们。以下是我的代码: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")
3条答案
按热度按时间agyaoht71#
原则
Robert C. Martin对依赖倒置原则的定义包括两部分:
1.高级模块不应该依赖于低级模块。两者都应该依赖于抽象。
1.抽象不应该依赖于细节。细节应该依赖于抽象。
只是澄清一下。。一个模块可以是一个函数,一个类,一个文件...一段代码
错误
假设你有一个程序需要你烤面包。
在更高的级别上,可以调用
cook()
实现这一点的一个糟糕的方法是创建一个既能烹饪又能创建面包的函数。
这可不妙。。
正如你所看到的,
cook
函数*依赖于Bread
。如果你想烤饼干怎么办?
一个新手错误**是像这样添加字符串参数:
这显然是错误的。因为通过添加更多的食物,你改变了你的代码,你的代码变得一团糟,有很多if语句。它几乎打破了所有的原则。
解决方案
因此,您需要
cook
函数,这是一个更高级别的模块,而不是依赖于Bread
或Cookies
等更低级别的模块所以我们唯一需要的是我们可以烤的东西。我们会烤的。现在正确的方法是通过实现一个接口。在Python中,这不是必要的,但强烈建议保持代码整洁和面向未来!
如果它看起来像鸭子,游泳像鸭子,嘎嘎叫像鸭子,那么它可能是一只鸭子。
现在让我们反转依赖关系!
现在
cook
函数依赖于抽象**。不是在面包上,不是在饼干上,而是在抽象上。任何任何任何Bakable
现在都可以烘焙。通过实现接口,我们确信每个
Bakable
都将有一个bake()
方法来做一些事情。但是现在
cook
函数不需要知道了。cook函数将烘焙任何Bakable
。依赖现在转到客户端。客户是一个想烤东西。客户端是一些将要使用
cook
函数的代码。客户知道要烤什么。现在通过查看
cook
函数,客户端知道cook
函数等待接收Bakable
,并且只接收Bakable
。让我们来制作一些面包。
现在让我们创建一些cookie!
好的!现在我们来煮它们。
iugsix8n2#
使用它:
n9vozmp43#
(非常)可以说,Python的一个伟大特性是动态/鸭子类型。只要传入的对象实现了函数所需的特定方法和成员,一切都应该正常工作。
通过使用静态类型检查器和类型库的Protocol特性,我们可以实现依赖反转,同时仍然享受动态类型的自由。值得注意的是,使用
ABC
和@abstractmethod
不会检查实现方法上的兼容签名,也不允许duck类型。当使用
Protocol
时,实现的类甚至不需要引用或知道协议/接口,允许使用外部类。在有多个开发者的代码库中,提交钩子可以验证任何内部实现类的任何更改。使用协议
使用ABC的进一步实验
静态类型检查下面的代码段(在Vscode中使用PyLance)很乐意允许行
它没有正确地实现bake方法(返回
int
而不是str
)。并且静态类型检查器在第行标记错误,
这是完全有效/“安全”的Python代码,将运行。有时候你可能想强制一个对象实现一个特定的抽象基类,但我认为这通常不是依赖倒置的必要条件。