[객체지향] SOLID - DIP 의존성 역전 원칙

주형(Jureamer)·2023년 6월 1일
0

객체지향 원칙(SOLID)이란?

객체지향 원칙이란 "로버트 마틴"이 소개한 객체 지향 프로그래밍 및 설계의 5가지 기본 원칙을 말하며, 5가지의 원칙의 앞 글자를 따서 “SOLID”라고 부른다.

- SRP (Single Responsibility Principle) : 단일 책임 원칙
- OCP (Open/Closed Principle) : 개방/폐쇄 원칙
- LSP (Liskov Substitution Principle) : 리스코프 치환 원칙
- ISP (Interface Segregation Principle) : 인터페이스 분리 원칙
- DIP (Dependacny Inversion Principle) : 의존관계 역전 원칙

DIP(의존관계 역전 원칙) 이란?

객체 지향의 5대 원칙인 SOLID 중 "D"를 담당하는 의존성 역전 원칙 "DIP"에 대해 소개 해보자 한다. DIP는 아래와 같은 핵심 정의를 갖는다.

1. 상위 모듈은 하위 모듈에 의존해서는 안된다.
2. 추상화는 세부 사항에 의존해서는 안된다.

-> 즉, 변화하기 쉬운 것에 의존하기보다는 변화하지 않는 것에 의존하라는 원칙이다.

이 정의에서 중요한 사항은 종속성의 방향만 변경하는 것이 아닌 상위 및 하위 수준 모듈이 추상화에 의존한다는 것이다. 추상화를 중간에 도입하여 상위 모듈과 하위 모듈 사이에 의존성을 분할하는 것이다.

DIP를 만족하면, 의존성 주입(Dependency Injection)을 통해 변화를 쉽게 수용할 수 있는 코드를 작성할 수 있다.

의존성 주입을 이용하면, DIP를 만족하는 변화에 유연한 시스템이 된다.

  • 만약 어떤 클래스에서 상속받아야 한다면, 부모 클래스를 추상 클래스로 만든다.
  • 어떤 클래스의 참조(reference)를 가져야 한다면, 참조 대상이 되는 클래스를 추상 클래스로 만든다.
  • 어떤 메소드를 호출해야 한다면, 호출되는 메소드를 추상 메소드로 만든다.

DIP 예시 살펴보기

DIP가 적용되지 않은 코드

사람과 취미에 대해 Kotlin으로 작성된 예시 코드이다.

Person Class가 구체적인 취미인 "Painting"과 "Cooking Hobby"에 의존하고 있는 것을 볼 수 있다.

즉, 결합도가 높아지게 되어 변경이 어려워지고 코드의 재사용성도 감소하게 된다.

// 구체적인 취미 클래스
class PaintingHobby {
    fun enjoyPainting(): String {
        return "I enjoy painting!"
    }
}

class CookingHobby {
    fun enjoyCooking(): String {
        return "I enjoy cooking!"
    }
}

// 사람 클래스
class Person {
    private val paintingHobby = PaintingHobby()
    private val cookingHobby = CookingHobby()

    fun doPaintingHobby(): String {
        return paintingHobby.enjoyPainting()
    }

    fun doCookingHobby(): String {
        return cookingHobby.enjoyCooking()
    }
}

// 메인 함수
fun main() {
    val person = Person()
    println(person.doPaintingHobby())
    println(person.doCookingHobby())
}

DIP가 적용된 코드

다음은 DIP가 적용된 코드를 살펴보자.

// 인터페이스 정의
interface Hobby {
    fun enjoy(): String
}

// 구체적인 취미 클래스
class PaintingHobby : Hobby {
    override fun enjoy(): String {
        return "I enjoy painting!"
    }
}

class CookingHobby : Hobby {
    override fun enjoy(): String {
        return "I enjoy cooking!"
    }
}

// 사람 클래스
class Person(private val hobby: Hobby) {
    fun doHobby(): String {
        return hobby.enjoy()
    }
}

// 메인 함수
fun main() {
    val paintingHobby = PaintingHobby()
    val person1 = Person(paintingHobby)
    println(person1.doHobby())

    val cookingHobby = CookingHobby()
    val person2 = Person(cookingHobby)
    println(person2.doHobby())
}
  1. Hobby의 인터페이스를 정의하여 다양한 취미를 추상화하였다.
  2. Person 클래스는 Hobby 인터페이스에 의존하며 doHobby 함수를 호출하여 취미를 즐길 수 있도록 되었다!(야호!)

이토록 상위, 하위 모델끼리 직접 의존하지 않고 중간에 인터페이스 계층을 의존하게 하면서 결합도가 낮아지게 만들고 이후 Game, Swim 등 취미를 쉽게 추가할 수 있어 재사용성도 높일 수 있다.


마무리

어쩌다보니 객체지향 5가지 원리 중 마지막인 의존성 역전 원칙을 제일 먼저 작성하게 되었다! 남은 4개라도 순서에 맞춰 올려봐야겠다.

그리고 이러한 DIP 원칙은 "개방/폐쇄 원칙""리스코프 치환 원칙"을 준수하게 된다면 자동적으로 준수된다고 한다.

각각의 원칙은 다른 원칙과 연관성이 있으니 이러한 부분을 잘 이해하고 실전에 적용시켜야할 것 같다.


참고

  1. SOLID Design Principles Explained: Dependency Inversion Principle with Code Examples
  2. [OOP] 객체지향 프로그래밍의 5가지 설계 원칙, 실무 코드로 살펴보는 SOLID
  3. [OOP] 객체지향 5원칙(SOLID) - 의존성 역전 원칙
profile
작게라도 꾸준히 성장하는게 목표입니다.

0개의 댓글