依赖倒置原则
775字约3分钟
设计模式
2022-03-25
原始定义
高层模块不应该依赖低层模块, 两者都应该依赖其抽象
抽象不应该依赖细节, 细节应该依赖抽象
举个例子
- 假设有一个人要抽一份明星的图
class Iger():
def extract_star():
Star().extract()
class Star()
def extract():
print("抽明星图")
- 业务不断扩展, 小明要抽的具体的图越来越多, 有 20 个具体的图
class Iger():
def extract_star():
Star().extract()
# 20个抽具体图的方法
def extract_unicom():
Unicom().extract()
class Star():
def extract():
print("抽明星的图")
# 20个抽具体图的方法
class Unicom():
def extract():
print("抽联通的图")
每一次业务的增加, 都要修改原有的顶层类 Iger, 增加新的 extract 方法
为什么需要依赖倒置原则
原因一: 有效控制影响范围
因为「业务的扩展」, 要从底层实现到高层调用依次地修改代码
Iger 类中新增 extract_unicom
方法, 也需要在高层调用中增加调用系统发布后, 其实是非常不稳定的
实际的情况和实际的软件环境要复杂得多.
最理想的情况就是, 我们已经编写好的代码可以 "万年不变", 这就意味着已经覆盖的单元测试可以不用修改, 已经存在的行为可以保证保持不变, 这就意味着「稳定」. **任何代码上的修改带来的影响都是有未知风险的, 不论看上去多么简单. **
原因二: 增强代码可读性和可维护性
另外一点, 你有没有发现其实加上新增的图的抽取, 这些抽图本质上行为都是一样的, 如果我们任由这样行为近乎一样的代码在我们的类里面肆意扩展的话, 很快我们的类就会变得臃肿不堪, 等到我们意识到不得不重构这个类以缓解这样的情况的时候, 或许成本已经变得高得可怕了.
原因三: 降低类间的耦合性
出现了一连串的对象依赖, 从而造成了严重的耦合灾难.
怎么做
面向抽象类的编程来降低类间的耦合性
class Iger():
def extract(graph):
graph.extract()
每次增加新的图类型, 不需要修改上层的 Iger 类
只需要增加新的图类型, 通过参数传递的方式联系
class Graph(abc.ABC):
def extract():
pass
class Star(Graph):
def extract():
print("抽明星的图")
class Unicom(Graph):
def extract():
print("抽联通的图")
iger = Iger()
iger.extract(Star())
iger.extract(Unicom())
至此, Iger 就只是依赖 抽象类 图, 而不是具体的 明星图、联通图等.
对于新的图, 都只需要新建一个类, 通过参数传递的方式告诉它, **而不需要修改底层的代码. **
抽图中的代码例子
上层: GraphExtractor4DB
底层: PG、Hive、Oracle等
GraphExtractor4DB 只依赖底层的抽象 DataDriver