Cute Light Pink Flying Butterfly DIP 의존성 역전 원칙 | SOLID 설계 원칙 :: 놀면서 돈벌기
본문 바로가기
IT/Architecture

DIP 의존성 역전 원칙 | SOLID 설계 원칙

by esclife_ 2025. 10. 26.
반응형

DIP(Dependency Inversion Principle) - 의존성 역전 원칙

고수준 모듈은 저수준 모듈에 의존해서는 안 되며, 두 모듈 모두 추상화에 의존해야 한다

  • 어떤 기능을 참조해서 사용해야하는 상황이 생기면, 그 Class를 직접 참조하는 것이 아니라 그 대상의 상위 요소(추상 클래스 or 인터페이스)로 참조하라는 원칙
  • 구현클래스에 의존하지 말고, 상위 인터페이스에 의존하라는 뜻
  • 의존 관계를 맺을 때 변화성이 낮고 변화하기 어려운것에 의존하라는 것
  • 의존 역전 원칙의 지향점은 각 클래스간의 결합도(coupling)을 낮추는 것

Inpa 블로그 참조


DIP 위반 사례 ❌

요리사(고수준 모듈)가 핸드 믹서(저수준 모듈)라는 구체 클래스에 직접 의존하고 있습니다.

// 저수준 모듈 (구체 클래스, 자주 바뀔 수 있음)
class HandMixer {
    public void whipCream() {
        System.out.println("Hand Mixer: 휘핑 크림을 만듭니다.");
    }
}

// 고수준 모듈 (비즈니스 로직, 자주 바뀌지 않아야 함)
class Chef {
    // ❌ Chef가 HandMixer라는 구체 클래스에 직접 의존함
    private HandMixer mixer; 

    public Chef() {
        // ❌ Chef가 직접 HandMixer를 생성함 (강한 결합)
        this.mixer = new HandMixer(); 
    }

    public void makeCake() {
        System.out.println("Chef: 케이크 반죽을 준비합니다.");
        // 구체적인 도구의 메서드를 호출
        this.mixer.whipCream(); 
        System.out.println("Chef: 케이크를 완성합니다.");
    }
}

public class DIPViolationExample {
    public static void main(String[] args) {
        Chef baker = new Chef();
        baker.makeCake();
        
        // 문제: 만약 HandMixer가 고장나서 StandMixer로 바꿔야 한다면, 
        // Chef 클래스 내부 코드를 수정해야 함. (OCP 위반)
    }
}

위반 이유: Chef 클래스(고수준)가 HandMixer 클래스(저수준)에 직접 의존하고 있습니다. 저수준 모듈인 믹서가 변경될 때마다 고수준 모듈인 Chef의 코드를 수정해야 하므로 결합도가 높습니다.


DIP 준수 사례 ✅

요리사(고수준 모듈)와 믹서(저수준 모듈) 모두 믹싱 도구(추상화/인터페이스)에 의존하도록 의존 관계를 역전시킵니다.

// 1. 추상화 (상위 요소, 변화하기 어려움)
interface IMixer {
    void whipCream();
}

// 2. 저수준 모듈 (구현 클래스, 변화가 쉬움)
class HandMixer implements IMixer {
    @Override
    public void whipCream() {
        System.out.println("Hand Mixer: 휘핑 크림을 만듭니다.");
    }
}

// 저수준 모듈 (새로운 구현체 추가)
class StandMixer implements IMixer {
    @Override
    public void whipCream() {
        System.out.println("Stand Mixer: 자동으로 휘핑 크림을 만듭니다.");
    }
}

// 3. 고수준 모듈 (비즈니스 로직)
class Chef {
    // ✅ Chef가 IMixer라는 추상화에 의존함
    private IMixer mixer; 

    // ✅ 의존성 주입(Dependency Injection): 외부에서 IMixer 구현체를 받음 (느슨한 결합)
    public Chef(IMixer mixer) { 
        this.mixer = mixer;
    }

    public void makeCake() {
        System.out.println("Chef: 케이크 반죽을 준비합니다.");
        // 추상적인 도구의 메서드를 호출
        this.mixer.whipCream(); 
        System.out.println("Chef: 케이크를 완성합니다.");
    }
}

public class DIPComplianceExample {
    public static void main(String[] args) {
        // 클라이언트 코드에서 어떤 믹서를 사용할지 결정 (제어의 역전)
        IMixer handMixer = new HandMixer();
        Chef baker1 = new Chef(handMixer);
        baker1.makeCake(); 

        System.out.println("--- 믹서를 교체합니다 ---");
        
        // 새로운 믹서로 변경해도 Chef 클래스는 수정할 필요가 없음 (OCP, DIP 준수)
        IMixer standMixer = new StandMixer();
        Chef baker2 = new Chef(standMixer);
        baker2.makeCake();
    }
}

 

Chef 클래스는 이제 구체적인 믹서 종류를 모르고, 오직 IMixer 인터페이스의 계약에만 의존합니다. 믹서의 종류가 바뀌거나 새로운 믹서가 추가되어도 Chef 클래스는 수정할 필요가 없습니다. 이처럼 의존 관계가 추상화(Interface)를 향하도록 역전되었습니다.


DIP 위반을 한 경우들

DIP는 고수준 모듈이 저수준 모듈의 구체적인 상세에 의존할 때 위반됩니다. 일반적으로 다음과 같은 상황에서 DIP가 위반됩니다.

1. 구체 클래스에 직접 의존하는 경우

  • 설명: 고수준 클래스가 필드 타입이나 메서드 매개변수로 인터페이스 대신 특정 구현 클래스를 사용하는 경우입니다.
  • 예시: LoggerService 클래스 내부에 FileLogger라는 구체적인 로거 클래스 객체를 직접 생성하거나 사용하는 경우. 나중에 로깅 방식을 DatabaseLogger로 바꾸려면 LoggerService 클래스 내부를 수정해야 합니다.

2. new 키워드를 사용하여 구체 객체를 직접 생성하는 경우

  • 설명: 고수준 모듈 내부에서 new 키워드를 사용하여 저수준 모듈의 인스턴스를 직접 생성하는 경우, 이는 해당 구체 클래스에 강하게 종속됩니다.
  • 예시: 위반 예제에서 new HandMixer()를 Chef 클래스 생성자 내부에서 호출한 경우. 제어권(Control)이 Chef에게 있어 강하게 결합됩니다.

3. 정적 메서드에 과도하게 의존하는 경우

  • 설명: 특정 유틸리티 클래스나 저수준 모듈의 static 메서드를 직접 호출하여 사용하는 경우. 정적 메서드는 인터페이스로 대체되거나 오버라이드될 수 없어 추상화를 통한 유연한 확장이 불가능해집니다.
  • 예시: OrderProcessor 클래스가 ValidationUtils.isValid(data)와 같이 정적 유틸리티 메서드를 직접 호출하는 경우. OrderProcessor는 이 구체적인 ValidationUtils 클래스에 강하게 의존하게 됩니다.

이러한 위반 사례들은 모두 추상화 대신 구체적인 상세에 의존함으로써, 결합도를 높이고 확장성(OCP)을 떨어뜨리는 결과를 낳습니다.


참고 자료

 

💠 객체 지향 설계의 5가지 원칙 - S.O.L.I.D

객체 지향 설계의 5원칙 S.O.L.I.D 모든 코드에서 LSP를 지키기에는 어려움. 리스코프 치환 원칙에 따르면 자식 클래스의 인스턴스가 부모 클래스의 인스턴스를 대신하더라도 의도에 맞게 작동되어

inpa.tistory.com

 

 

💠 완벽하게 이해하는 DIP (의존 역전 원칙)

의존 역전 원칙 - DIP (Dependency Inversion Principle) DIP 원칙이란 객체에서 어떤 Class를 참조해서 사용해야하는 상황이 생긴다면, 그 Class를 직접 참조하는 것이 아니라 그 대상의 상위 요소(추상 클래스

inpa.tistory.com

 

반응형