我试图通过本教程中的一些简单但具体的代码和类(在Python中实现)来理解依赖倒置原理(DIP)。我总结一下(用我自己的评论和理解),以减轻您经历整个事情的痛苦。
基本上,我们正在构建一个货币转换器应用程序,其中我们将main应用程序逻辑与货币转换器本身分开。代码(我的一些注释和文档字符串)如下。
#!/usr/bin/env python3
# encoding: utf-8
"""Currency converter application using some exchange API."""
class FXConverter:
"""The converter class."""
def convert(self, from_currency, to_currency, amount):
"""
Core method of the class. Assume the magic number 1.2 is from some API like
Oanda
"""
print(f'{amount} {from_currency} = {amount * 1.2} {to_currency}')
return amount * 1.2
class App:
"""The Application"""
def start(self):
"""The main method to create and invoke the converter object."""
converter = FXConverter()
converter.convert('EUR', 'USD', 100)
if __name__ == '__main__':
app = App()
app.start()
现在,教程声称(直接引用)
将来,如果FX的API发生变化,就会破坏代码。另外,如果您想使用不同的 API,则需要更改 App 类。
所以他们提出了这个。
#!/usr/bin/env python3
# encoding: utf-8
"""Currency converter application using dependency inversion."""
from abc import ABC
class CurrencyConverter(ABC):
def convert(self, from_currency, to_currency, amount) -> float:
pass
class FXConverter(CurrencyConverter):
def convert(self, from_currency, to_currency, amount) -> float:
print('Converting currency using FX API')
print(f'{amount} {from_currency} = {amount * 1.2} {to_currency}')
return amount * 1.2 # The tutorial seems to have a typo here.
class App:
def __init__(self, converter: CurrencyConverter):
self.converter = converter
def start(self):
self.converter.convert('EUR', 'USD', 100)
if __name__ == '__main__':
converter = FXConverter()
app = App(converter)
app.start()
对我来说,这句话似乎并不真实,这正是我无法理解 DIP 的核心原因。即使
FXConverter
使用不同的交换 API(假设 Bloomberg 而不是 Oanda),更改不会保持本地化到 convert
方法吗?只要convert
方法保持签名
convert(str, str, float)->float # The strings must be valid currency names
App.start
应该很高兴。维护这个有效方法签名的必要性是
这就是为什么我根本看不到 DIP 如何有助于更好的解耦,而我们真正需要的是遵守静态类型语言中的函数/方法签名?
一般来说,当
A
调用B
(对象方法或函数等)时,我们可以假设
A
不了解 B
B
不知道 A
对结果做了什么以便自动执行所需的解耦?
这个解释实际上听起来有点令人困惑。
如果您的
FXConverter
实现发生变化 App
也没关系,即使没有 DI。
如果
FXConverter
的 API 发生变化,App
会中断,无论有或没有 DI。
但是 DI 使您可以选择将
FXConverter
替换为其他类别,例如改进的 GXConverter
而不触及 App
。
这就是 DI 的意义所在。
您想要交换实现的一个典型示例是测试,其中您用模拟替换类(使用术语松散),这可能提供预设结果、记录和/或检查调用或使用简化的算法。