재우니의 블로그

 

Dependency Inversion Principle (DIP)

 

 

종속성 반전 원칙(DIP)


종속성 반전 원칙(DIP)은 상위 모듈이 하위 모듈에 종속되어서는 안 되며, 둘 다 추상화에 종속되어야 한다는 원칙입니다.
이 원칙은 구성 요소 간의 분리를 촉진하여 코드베이스가 변경에 더 잘 적응하고 단위 테스트를 용이하게 합니다.

 

 

코드는 C#으로 작성되었고, DIP를 준수하기 위해서는 추상화된 인터페이스나 추상 클래스를 도입하여 의존성을 역전된 코드입니다.

 

Switch 클래스가 LightBulb 클래스에 직접 의존하지 않고, 대신 ISwitchable 인터페이스에 의존하므로 DIP를 준수하게 됩니다. 이렇게 하면 Switch 클래스가 다양한 종류의 ISwitchable 구현을 사용할 수 있어 코드의 유연성이 향상됩니다.

 

using System;

public interface ISwitchable
{
    void TurnOn();
    void TurnOff();
    bool IsOn { get; }
}

public class LightBulb : ISwitchable
{
    private bool isOn;

    public void TurnOn()
    {
        isOn = true;
        Console.WriteLine("Light bulb is now ON.");
    }

    public void TurnOff()
    {
        isOn = false;
        Console.WriteLine("Light bulb is now OFF.");
    }

    public bool IsOn => isOn;
}

public class Switch
{
    private readonly ISwitchable device;

    public Switch(ISwitchable device)
    {
        this.device = device;
    }

    public void Toggle()
    {
        if (device.IsOn)
            device.TurnOff();
        else
            device.TurnOn();
    }

    public bool IsLightBulbOn()
    {
        return device.IsOn;
    }
}

class Program
{
    static void Main()
    {
        // Switch 인스턴스 생성
        var lightSwitch = new Switch(new LightBulb());

        // 초기 상태 출력
        Console.WriteLine("Initial state:");
        Console.WriteLine("Light bulb is " + (lightSwitch.IsLightBulbOn() ? "ON" : "OFF"));

        // Toggle 메서드 호출 및 상태 변경
        lightSwitch.Toggle();

        // 변경된 상태 출력
        Console.WriteLine("\nState after Toggle:");
        Console.WriteLine("Light bulb is " + (lightSwitch.IsLightBulbOn() ? "ON" : "OFF"));
    }
}

 

 

 

 

위반의 예

  1. 상위 레벨 코드가 하위 레벨 코드에 직접 종속됨 - 상위 레벨 모듈이 하위 레벨 데이터베이스 모듈에 직접 액세스하여 긴밀한 결합을 생성합니다.

  2. 상위 수준 코드의 구체적인 인스턴스화 - 종속성 주입에 의존하지 않고 상위 수준 모듈 내에서 구체적인 종속성을 인스턴스화하는 서비스입니다.

  3. 추상화 부족 - 인터페이스나 추상화를 사용하지 않고 외부 서비스의 특정 구현에 의존하는 클래스로, 구성 요소를 교체하거나 테스트하기 어렵게 만듭니다.

 

위반한 코드 샘플

using System;

public class LightBulb
{
    private bool isOn;

    public void TurnOn()
    {
        isOn = true;
        Console.WriteLine("Light bulb is now ON.");
    }

    public void TurnOff()
    {
        isOn = false;
        Console.WriteLine("Light bulb is now OFF.");
    }

    public bool IsOn => isOn;
}

public class Switch
{
    private readonly LightBulb bulb;

    public Switch()
    {
        bulb = new LightBulb();
    }

    public void Toggle()
    {
        if (bulb.IsOn)
            bulb.TurnOff();
        else
            bulb.TurnOn();
    }
}

class Program
{
    static void Main()
    {
        // Switch 인스턴스 생성
        var lightSwitch = new Switch();

        // 초기 상태 출력
        Console.WriteLine("Initial state:");
        Console.WriteLine("Light bulb is " + (lightSwitch.IsLightBulbOn() ? "ON" : "OFF"));

        // Toggle 메서드 호출 및 상태 변경
        lightSwitch.Toggle();

        // 변경된 상태 출력
        Console.WriteLine("\nState after Toggle:");
        Console.WriteLine("Light bulb is " + (lightSwitch.IsLightBulbOn() ? "ON" : "OFF"));
    }
}

 

 

해당 코드에서 Switch 클래스가 LightBulb 클래스에 직접 의존하고 있습니다. 이는 고수준 모듈(Switch)이 저수준 모듈(LightBulb)에 의존한다는 원칙을 위반합니다. 예를 들어, 만약 Switch가 다른 종류의 LightBulb 클래스를 사용하려면 코드를 수정해야 합니다. 이는 변경에 대한 유연성이 떨어진다는 의미입니다.

DIP를 준수하기 위해서는 추상화된 인터페이스나 추상 클래스를 도입하여 의존성을 역전시켜야 합니다.