目录
设计模式-02 设计模式-依赖倒转原则案例分析
1.定义
2.内涵
3.案例对比
4.注意事项
5.最佳实践
6.总结
依赖倒转原则(Dependency Inversion Principle,简称DIP)高层级的模块不能依赖底层级模块的,两种层级的模块应该依赖抽象,抽象层不能依赖具体实现层,具体实现应该依赖抽象
通俗来说,DIP 意味着:
客户端代码(高层模块)不应该直接依赖于具体实现(低层模块)。
相反,客户端代码应该依赖于一个抽象,而抽象再依赖于具体实现。
+--------------+
| 客户端代码 |
+--------------+
|
V
+--------------+
| 抽象类 |
+--------------+
|
V
+--------------+
| 具体实现类 |
+--------------+
说明:
DIP 的定义出发点是:
软件的灵活性:当依赖关系是固定的时,很难在不影响其他模块的情况下修改软件。
软件的可测试性:当客户端代码直接依赖于具体实现时,很难对客户端代码进行单元测试。
错误的设计,经常是依赖与具体的实现。而不是依赖与接口层。以下就具体案例进行的举例说明。
bad 设计
#include
#include
#include
#include
#include
#include
using namespace std;
enum class Relationship{
parent,
child,
sibling,
};
struct Person{
string name;
};
struct Relationships // 底层模块
{
vector> relations;
void add_parent_add_child(const Person& parent, const Person& child){
relations.push_back({parent, Relationship::parent, child});
relations.push_back({child, Relationship::child, parent});
}
};
struct Research // 上层模块,依赖与底层的具体实现,而不是抽象接口,违反了依赖倒转原则
{
Research(Relationships& relationships){
auto& relations = relationships.relations;
for(auto& [first, rel, second]: relations){
if(first.name == "Hohn" && rel == Relationship::parent){
cout<<"Hohn has child :"<< second.name<< endl;
}
}
}
};
int main() {
Person parent{"Hohn"};
Person child1{"Dhris"},child2{"Watt"};
Relationships relationships;
relationships.add_parent_add_child(parent,child1);
relationships.add_parent_add_child(parent,child2);
Research _(relationships);
return 0;
}
好的设计
#include
#include
#include
#include
#include
#include
using namespace std;
enum class Relationship{
parent,
child,
sibling,
};
struct Person{
string name;
};
struct RelationshipBrowser{
virtual vector find_all_children_of(const string& name) = 0;
};
struct Relationships :RelationshipBrowser // 底层模块
{
vector> relations;
void add_parent_add_child(const Person& parent, const Person& child){
relations.push_back({parent, Relationship::parent, child});
relations.push_back({child, Relationship::child, parent});
}
vector find_all_children_of(const string &name) override {
vector result;
for(auto&& [first,rel,second] : relations) {
if(first.name == name && rel == Relationship::parent){
result.push_back(second);
}
}
return result;
}
};
struct Research // 上层模块不在依赖 Relationships ,而是依赖接口:RelationshipBrowser
{
Research(RelationshipBrowser& browser){
for(auto& child: browser.find_all_children_of("Hohn")){
cout<<"Honh has child:"<< child.name<
两种设计思路输出结果一样,但是好的设计遵循了依赖倒转原则,代码更灵活
Honh has child:Dhris
Honh has child:Watt
在具体开发中,应用依赖倒转原则(DIP)时需要注意以下几点:
DIP 有以下优点:
DIP 并不是万能的,在某些情况下可能会有一些局限性:
(1)轻微的性能开销:使用 DIP 可能会有轻微的性能开销,因为需要额外的开销来管理依赖项。
(2)过度抽象:过度抽象可能会使代码难以理解和维护。
过度抽象就会使得系统复杂化,不便于后续优化。