Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> C#

Dependency Inversion Principle (DIP) คืออะไร และการนำไปใช้ใน C#

Dependency Inversion Principle (DIP) เป็นหลักการหนึ่งใน SOLID Principles:

1. High-level modules ไม่ควรพึ่งพา Low-level modules ทั้งคู่ควรพึ่งพา Abstractions
2. Abstractions ไม่ควรพึ่งพา Details Details ควรพึ่งพา Abstractions

เป้าหมาย: ลด Coupling ระหว่างโมดูล ทำให้เปลี่ยน Implementation ได้ง่าย

ตัวอย่าง: ก่อนใช้ DIP (Violation)

// Low-level modules
public class Email
{
    public string To { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
    public void Send() { /* Send email logic */ }
}

public class SMS
{
    public string PhoneNumber { get; set; }
    public string Message { get; set; }
    public void Send() { /* Send SMS logic */ }
}

// High-level module พึ่งพา Concrete Classes โดยตรง
public class Notification
{
    private readonly Email _email = new Email(); // ทำให้ติดกับ Email
    private readonly SMS _sms = new SMS();       // ทำให้ติดกับ SMS

    public void Send()
    {
        _email.Send();
        _sms.Send();
    }
}

ปัญหา: Notification ติดกับ Email และ SMS โดยตรง (Tight Coupling) หากเพิ่ม Push Notification ต้องแก้ Notification

ตัวอย่าง: หลังใช้ DIP (Compliant)

// 1. Abstraction (Interface)
public interface IMessage
{
    void Send();
}

// 2. Low-level Modules Implement Abstraction
public class Email : IMessage
{
    public string To { get; set; }
    public string Subject { get; set; }
    public string Body { get; set; }
    public void Send() { /* Send email */ }
}

public class SMS : IMessage
{
    public string PhoneNumber { get; set; }
    public string Message { get; set; }
    public void Send() { /* Send SMS */ }
}

// 3. เพิ่มใหม่ได้ง่าย ไม่ต้องแก้โค้ดเดิม
public class PushNotification : IMessage
{
    public string DeviceToken { get; set; }
    public string Payload { get; set; }
    public void Send() { /* Send Push */ }
}

// 4. High-level Module พึ่งพา Abstraction (IMessage)
public class Notification
{
    private readonly IEnumerable<IMessage> _messages;

    // รับ Dependency ผ่าน Constructor (Constructor Injection)
    public Notification(IEnumerable<IMessage> messages)
    {
        _messages = messages;
    }

    public void Send()
    {
        foreach (var msg in _messages)
        {
            msg.Send(); // Polymorphism
        }
    }
}

การลงทะเบียนใน DI Container (ASP.NET Core)

services.AddTransient<IMessage, Email>();
services.AddTransient<IMessage, SMS>();
services.AddTransient<IMessage, PushNotification>();
services.AddTransient<Notification>(); // จะได้ IEnumerable<IMessage> อัตโนมัติ

ประโยชน์

  • High-level Module (Notification) ไม่รู้จัก Low-level Details (Email, SMS)
  • เพิ่ม/ลด/เปลี่ยน Channel ได้โดยไม่แก้ Notification
  • ทดสอบง่าย (Mock IMessage)
  • รองรับ Open/Closed Principle

สรุป

DIP = Programming to Interfaces, not Implementations ใช้ Constructor Injection + DI Container จะได้ DIP อย่างแท้จริง