본문 바로가기

Develop/소프트웨어 설계

[SOLID] Dependency Inversion 원칙

반응형

목차

Single Responsibility 원칙

Open/Closed 원칙

Liskov Substitution 원칙

Interface Segregation 원칙

Dependency Inversion 원칙

 

 

SOLID 의존관계 역전 원칙
Dependency Inversion 원칙

Dependency Inversion (의존관계 역전) 원칙

상위 모듈과 하위 모듈은 추상적인 약속을 기반으로 소통해야 한다.
자세한 구현은 추상적인 개념에 기반해야 한다.

Dependency Inversion (의존관계 역전) 원칙은 객체지향 설계의 핵심인 모듈 간 의존관계 해소를 강조하는 원칙이다. 모듈 간의 의존 관계가 낮아져야 모듈 세부사항을 변경하더라도, 전체 코드에 미치는 영향을 최소화하고 적은 양의 코드로 최대의 효과를 누릴 수 있다. 구체적으로 이야기하자면, 상위 모듈이 하위 모듈의 함수를 직접 호출하지 않는다. 대신 인터페이스와 같은 언어 기능을 활용하여 어떻게 소통할지를 정의한다. 상위 모듈은 약속된 함수를 호출하고, 하위 모듈은 약속된 함수를 구현한다. 따라서 상위 모듈과 하위 모듈은 서로에게 의존하기 보다, 인터페이스라는 약속에 의존한다. 약속만 지켜준다면 하위 모듈은 얼마든지 변화하고 대체되어도 된다. 또 하위 모듈을 구체적으로 파악해야 할 필요가 없다. 약속된 방식으로 의사소통만 하면 된다.

예제

이메일과 문자로 알림을 보내는 서비스를 개발한다고 가정하자.

public class Email
{
    public string ToAddress { get; set; }
    public string Subject { get; set; }
    public string Content { get; set; }
    public void SendEmail()
    {
        //이메일 보내기
    }
}

public class SMS
{
    public string PhoneNumber { get; set; }
    public string Message { get; set; }
    public void SendSMS()
    {
        //문자 보내기
    }
}

public class Notification
{
    private Email _email;
    private SMS _sms;
    public Notification()
    {
        _email = new Email();
        _sms = new SMS();
    }

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

상위 모듈인 Notification 클래스는 Email 클래스와 SMS 클래스라는 하위 모듈의 함수를 직접 호출하여 Send 함수를 구현한다. 따라서 상위 모듈과 하위 모듈 간에 의존 관계가 성립하게 된다. 만약 Email 클래스나 SMS 클래스가 생성되는 방법이 변경되거나, 함수명이 변경되거나, 함수를 호출하는 방법이 변경되어도 상위 모듈인 Notification 클래스도 함께 변경되어야 한다.

의존 관계를 해결하기 위해 아래와 같이 서로 어떻게 소통할지 약속해보자.

public interface IMessage
{
    void SendMessage();
}

이제 상위 모듈과 하위 모듈 모두 IMessage 인터페이스 약속을 통해 의사소통해보자.

public class Email : IMessage
{
    public string ToAddress { get; set; }
    public string Subject { get; set; }
    public string Content { get; set; }
    public void SendMessage()
    {
        //이메일 보내기
    }
}

public class SMS : IMessage
{
    public string PhoneNumber { get; set; }
    public string Message { get; set; }
    public void SendMessage()
    {
        //문자 보내기
    }
}
public class Notification
{
    private ICollection<IMessage> _messages;

    public Notification(ICollection<IMessage> messages)
    {
        this._messages = messages;
    }
    public void Send()
    {
        foreach(var message in _messages)
        {
            message.SendMessage();
        }
    }
}

이제 Notification 클래스는 하위 모듈이 어떻게 변경되든 영향을 받지 않는다. 심지어 새로운 하위 모듈이 생기더라도 IMessage 인터페이스 약속만 지켜준다면 문제없다.

원칙 적용 전에 심사숙고할 사항

소프트웨어 설계 원칙은 생산성이라는 이익을 얻기 위해 발생한 개념이다. 만약 원칙을 지키기 위해 팀원 간의 갈등이 생겨 팀이 와해되면 생산성은 곤두박질치게 된다. 따라서 원칙을 적용하기 전에 팀원과 자주 코드 리뷰를 하며 원칙이 필요함을 설득하는 정치적인 과정이 반드시 필요하다. 원칙을 불필요하다 생각하는 개발자는 그 나름의 이유가 있을 수 있고, 내가 틀렸을 수도 있고, 원칙을 잘못 이해했을 수도 있다. 나만 옳았고 너만 틀렸다 는 명확한 데이터와 분석을 기반해서 조심스럽게 접근해야지, 아니면 파괴적인 갈등만 유발한다.

반응형

'Develop > 소프트웨어 설계' 카테고리의 다른 글

[SOLID] Interface Segregation 원칙  (0) 2019.08.29
[SOLID] Liskov Substitution 원칙  (0) 2019.08.29
[SOLID] Open/Closed 원칙  (0) 2019.08.27
[SOLID] Single Responsibility 원칙  (0) 2019.08.16
[SOLID] SOLID 원칙이란  (2) 2019.08.15