의존 역전원칙이 무엇일까?

박승우·2024년 6월 11일

자 마흔 일곱 번째 키워드인 '의존 역전 원칙'을 알아 볼 것이다.

이번 키워드는 이전 키워드인 OCP처럼 SOLID원칙중 하나 이므로
직접 코드로 알아보았다.

의존 역전 원칙(Dependency Inversion Principle, DIP)이 뭐에요?

의존 역전 원칙(Dependency Inversion Principle, DIP)은 소프트웨어 모듈의 결합도를 낮추기 위한 설계 원칙이다.
다음 두가지의 규칙을 포함 한다고 한다.

  1. 고수준 모듈이 저수준 모듈에 의존해서는 안 된다. 둘 다 추상화에 의존해야 한다.
  • 고수준 모듈은 시스템의 주로 중요한 정책이나 로직을 포함있다.
  • 저수준 모듈은 실제 구현 세부 사항을 포함한다.
  • 고수준 모듈이 저수준 모듈에 의존하면, 저수준 모듈의 변경이 고수준 모듈에 영향을 미치기에, 이를 피하기 위해선 둘 다 추상화(인터페이스나 추상 클래스)에 의존하도록 해야 한다.
  1. 추상화는 세부 사항에 의존해서는 안 된다. 세부 사항이 추상화에 의존해야 한다.
  • 추상화된 인터페이스나 클래스가 구체적인 구현 세부 사항보다 상위에 있어야 한다.
  • 구체적인 구현이 추상화에 맞추어져야 한다.

예시 코드 - Unity - 적용 X

// Light 클래스가 구체적인 구현
public class Light : MonoBehaviour
{
    public void TurnOn()
    {
        Debug.Log("Light: Turned On");
    }

    public void TurnOff()
    {
        Debug.Log("Light: Turned Off");
    }
}

// Switch 클래스가 Light 클래스에 직접 의존
public class Switch : MonoBehaviour
{
    private Light _light;

    void Start()
    {
        _light = new Light();
    }

    public void Operate(string action)
    {
        if (action == "ON")
        {
            _light.TurnOn();
        }
        else if (action == "OFF")
        {
            _light.TurnOff();
        }
    }
}

위 코드는 Light Class와 Switch Class로 나뉘어 구현하였는데 Light 클래스에서 구체적인 구현을 하고
Switch클래스에서 직접 의존하여 구현시킨 결과이다.

적은 양의 구현이 되어있어서 수정이 많이 필요없지만 나중에 방대한 양이 들어간다고 가정했을 때
Light Class에서 수정이 되었다면 의존한 클래스에서도 수정을 해야할 상황이 생기기 때문에
의존성을 낮춰야 한다.

예시 코드 - Unity - 적용 O

public interface ISwitchable
{
    void TurnOn();
    void TurnOff();
}

public class Light : MonoBehaviour, ISwitchable
{
    public void TurnOn()
    {
        Debug.Log("Light: Turned On");
    }

    public void TurnOff()
    {
        Debug.Log("Light: Turned Off");
    }
}

public class Switch : MonoBehaviour
{
    private ISwitchable _device;

    // 의존성을 주입하는 생성자
    public Switch(ISwitchable device)
    {
        _device = device;
    }

    public void Operate(string action)
    {
        if (action == "ON")
        {
            _device.TurnOn();
        }
        else if (action == "OFF")
        {
            _device.TurnOff();
        }
    }
}

public class GameManager : MonoBehaviour
{
    void Start()
    {
        ISwitchable light = new Light();
        Switch lightSwitch = new Switch(light);

        lightSwitch.Operate("ON");
        lightSwitch.Operate("OFF");
    }
}

코드 설명

  1. ISwitchable 인터페이스를 정의하여 TurnOn과 TurnOff 메서드를 선언
  2. Light 클래스는 ISwitchable 인터페이스를 구현하여 구체적인 동작을 제공
  3. Switch 클래스는 ISwitchable 인터페이스에 의존하도록 설계되었습니다. 따라서 Switch 클래스는 Light 클래스의 구체적인 구현에 의존하지 않고 추상화된 인터페이스에 의존
  4. GameManager 클래스는 유니티의 Start 메서드에서 Light 객체를 생성하고 이를 Switch 객체에 주입합니다. 이렇게 의존성을 주입함으로써 DIP를 따르는 설계를 구현

이렇게 설계하면 나중에 Light와 다른 ISwitchable 구현을 추가할 때, Switch 클래스를 전혀 수정하지 않아도 된다.

결론 - 느낀 점

이렇게 인터페이스를 쪼개는 이유를 여기서 알았다. 쪼개는 이유는 어디든 있고 이렇게 쪼개야
관리측면에서 편하기 때문에 사용하는 것 같다.

profile
게임을 좋아하는 사람 입니다!

0개의 댓글