-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathDependencyInversionPrinciple.cpp
More file actions
160 lines (134 loc) · 3.85 KB
/
DependencyInversionPrinciple.cpp
File metadata and controls
160 lines (134 loc) · 3.85 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
// Dependency Inversion Principle
// 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.
#include <iostream>
#include <memory>
#include <string>
#include <vector>
// ========================
// Without DIP (Violation)
// ========================
// Low-level module
class MySQLDatabase
{
public:
void save(const std::string &data)
{
std::cout << "[Bad] Saving '" << data << "' to MySQL database."
<< std::endl;
}
};
// High-level module directly depends on low-level module
class BadUserService
{
private:
MySQLDatabase db; // Tight coupling to MySQL!
public:
void createUser(const std::string &name)
{
std::cout << "[Bad] Creating user: " << name << std::endl;
db.save(name);
}
};
// Problem: If we want to switch to PostgreSQL or an in-memory DB for testing,
// we have to modify BadUserService. This violates DIP.
// ========================
// With DIP (Correct)
// ========================
// Abstraction (interface) that both high-level and low-level modules depend on
class IDatabase
{
public:
virtual ~IDatabase() = default;
virtual void save(const std::string &data) = 0;
virtual std::string getName() const = 0;
};
// Low-level module 1: MySQL
class MySQLDB : public IDatabase
{
public:
void save(const std::string &data) override
{
std::cout << "Saving '" << data << "' to MySQL." << std::endl;
}
std::string getName() const override { return "MySQL"; }
};
// Low-level module 2: PostgreSQL
class PostgreSQLDB : public IDatabase
{
public:
void save(const std::string &data) override
{
std::cout << "Saving '" << data << "' to PostgreSQL." << std::endl;
}
std::string getName() const override { return "PostgreSQL"; }
};
// Low-level module 3: In-Memory (for testing)
class InMemoryDB : public IDatabase
{
private:
std::vector<std::string> storage;
public:
void save(const std::string &data) override
{
storage.push_back(data);
std::cout << "Saving '" << data
<< "' to In-Memory DB. (Total records: " << storage.size() << ")"
<< std::endl;
}
std::string getName() const override { return "InMemory"; }
};
// High-level module depends on abstraction, NOT on concrete implementations
class UserService
{
private:
IDatabase &db; // Depends on interface!
public:
// Dependency is INJECTED via constructor
UserService(IDatabase &database) : db(database) {}
void createUser(const std::string &name)
{
std::cout << "Creating user '" << name << "' using " << db.getName()
<< " database." << std::endl;
db.save(name);
}
};
// Another high-level module reusing the same abstraction
class LogService
{
private:
IDatabase &db;
public:
LogService(IDatabase &database) : db(database) {}
void log(const std::string &message)
{
std::cout << "Logging to " << db.getName() << ": " << message << std::endl;
db.save("[LOG] " + message);
}
};
int main()
{
std::cout << "=== Without DIP (Violation) ===" << std::endl;
BadUserService badService;
badService.createUser("John");
// Can't easily switch databases or mock for testing
std::cout << "\n=== With DIP (Correct) ===" << std::endl;
// Easily switch between different database implementations
MySQLDB mysql;
PostgreSQLDB postgres;
InMemoryDB inMemory;
// Same UserService, different databases — no code change needed
UserService service1(mysql);
service1.createUser("Alice");
std::cout << std::endl;
UserService service2(postgres);
service2.createUser("Bob");
std::cout << std::endl;
UserService service3(inMemory); // Great for testing!
service3.createUser("Charlie");
std::cout << "\n--- Log Service reuses the same abstraction ---" << std::endl;
LogService logger(inMemory);
logger.log("User Charlie was created");
return 0;
}