-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdependecnyInversion.ts
More file actions
105 lines (90 loc) · 3.56 KB
/
Copy pathdependecnyInversion.ts
File metadata and controls
105 lines (90 loc) · 3.56 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
/**
* Dependency Inversion Principle - CORRECT Implementation
*
* The Dependency Inversion Principle states that:
* 1. High-level modules should not depend on low-level modules. Both should depend on abstractions.
* 2. Abstractions should not depend on details. Details should depend on abstractions.
*
* This example demonstrates the CORRECT approach where:
* - DBService is the abstraction (interface/abstract class)
* - Both high-level (UserService, DBPersistence) and low-level (PostgresDatbase, DynamoDB)
* modules depend on the DBService abstraction
* - Dependencies are injected through constructors (Dependency Injection)
*/
/**
* Abstraction: DBService interface
* This is the contract that all database implementations must follow
* Both high-level and low-level modules depend on this abstraction
*/
abstract class DBService {
abstract saveToDb(data : string): void;
}
/**
* Low-level module: PostgreSQL database implementation
* CORRECT: Implements the DBService abstraction
* The concrete implementation depends on the abstraction, not the other way around
*/
class PostgresDatbase extends DBService{
public saveToDb(data : string) : void {
console.log("Saving ", data ,"to - PostgrsDB");
}
}
/**
* Low-level module: DynamoDB database implementation
* CORRECT: Implements the DBService abstraction
* The concrete implementation depends on the abstraction, not the other way around
*/
class DynamoDB extends DBService{
public saveToDb(data: string) : void {
console.log("Saving ", data, "to - DynamoDB");
}
}
/**
* High-level module: Database persistence layer
* CORRECT: Depends on the DBService abstraction, not concrete implementations
*
* Benefits:
* - Can work with ANY database that implements DBService
* - Easy to test with mock implementations
* - No need to modify this class when adding new databases
*/
class DBPersistence {
// Dependency Injection: DBService is injected through constructor
constructor(public dbService : DBService){}
public saveToDb(data : string):void {
this.dbService.saveToDb(data)
}
}
/**
* High-level module: UserService
* CORRECT: Depends on the DBService abstraction, not concrete implementations
*
* Benefits of this approach:
* 1. Loose coupling: UserService doesn't know about specific database implementations
* 2. Easy to test: Can inject mock DBService for unit testing
* 3. Flexible: Can easily switch databases by injecting different implementations
* 4. Follows Open/Closed Principle: Can add new databases without modifying UserService
* 5. Single responsibility: UserService only handles user logic, not database specifics
*/
class UserService {
/**
* Constructor uses Dependency Injection
* CORRECT: Accepts DBService abstraction instead of creating concrete instances
* This inverts the dependency - UserService no longer controls which database is used
*/
constructor(public username : string, public dbService : DBService){}
/**
* Store data using the injected database service
* This method works with ANY database that implements DBService
*/
public storeDataToDb(): void {
this.dbService.saveToDb(this.username);
}
}
// Create concrete database instances
const postgresDb = new PostgresDatbase()
const dynamoDb = new DynamoDB()
// Dependency Injection in action: UserService receives the database dependency
// We can easily switch from postgresDb to dynamoDb without changing UserService
const userService : UserService = new UserService("Sulaiman", postgresDb);
userService.storeDataToDb();