Dependency Inversion Principle (DIP):
- High-level modules không nên phụ thuộc vào low-level modules. Cả hai nên phụ thuộc vào abstractions (interface/abstract class).
- Abstractions không nên phụ thuộc vào details. Details nên phụ thuộc vào abstractions.
Mục tiêu: Giảm coupling giữa các module, dễ thay đổi implementation, dễ test.
Vi phạm DIP: High-level phụ thuộc trực tiếp Low-level
// TRƯỚC: Vi phạm DIP
public class Email
{
public void SendEmail() { /* ... */ }
}
public class SMS
{
public void SendSMS() { /* ... */ }
}
public class Notification
{
private readonly Email _email = new Email(); // Tự tạo dependency
private readonly SMS _sms = new SMS();
public void Send()
{
_email.SendEmail();
_sms.SendSMS();
}
}
// Notification cứng nhắc với Email, SMS. Thêm PushNotification? Phải sửa Notification.
Tuân thủ DIP: Phụ thuộc vào Abstraction
// 1. Abstraction (Interface)
public interface IMessage
{
void SendMessage();
}
// 2. Low-level modules implement abstraction
public class Email : IMessage
{
public void SendMessage() { /* Send email */ }
}
public class SMS : IMessage
{
public void SendMessage() { /* Send SMS */ }
}
// 3. High-level module phụ thuộc vào abstraction
public class Notification
{
private readonly ICollection<IMessage> _messages;
// Inject qua constructor (Constructor Injection)
public Notification(ICollection<IMessage> messages)
{
_messages = messages;
}
public void Send()
{
foreach (var msg in _messages)
msg.SendMessage();
}
}
// 4. Composition Root (Program.cs / Startup.cs)
class Program
{
static void Main()
{
var messages = new List<IMessage>
{
new Email(),
new SMS()
// Thêm PushNotification mới không sửa Notification
};
var notifier = new Notification(messages);
notifier.Send();
}
}
Lợi ích
- Decoupling: Notification không biết Email/SMS cụ thể.
- Open/Closed: Thêm kênh mới (Push, Slack...) không sửa Notification.
- Testability: Dễ mock
IMessagetrong unit test. - Parallel development: Team A làm Notification, Team B làm Email/SMS.
DIP vs DI (Dependency Injection)
DIP là nguyên tắc thiết kế (architecture level). DI là kỹ thuật triển khai (implementation level) để đạt được DIP. DI Container (Microsoft.Extensions.DependencyInjection, Autofac) tự động resolve dependency tại runtime.
Ví dụ với DI Container (ASP.NET Core)
// Startup.cs
services.AddTransient<IMessage, Email>();
services.AddTransient<IMessage, SMS>();
// Hoặc đăng ký nhiều:
services.AddTransient<IEnumerable<IMessage>>(sp =>
new List<IMessage> { sp.GetRequiredService<Email>(), sp.GetRequiredService<SMS>() });
// Controller
public class NotificationController : ControllerBase
{
private readonly IEnumerable<IMessage> _messages;
public NotificationController(IEnumerable<IMessage> messages) => _messages = messages;
public IActionResult Send() { foreach(var m in _messages) m.SendMessage(); return Ok(); }
}