객체지향 원칙(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를 만족하는 변화에 유연한 시스템이 된다.
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가 적용된 코드를 살펴보자.
// 인터페이스 정의
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())
}
이토록 상위, 하위 모델끼리 직접 의존하지 않고 중간에 인터페이스 계층을 의존하게 하면서 결합도가 낮아지게 만들고 이후 Game, Swim 등 취미를 쉽게 추가할 수 있어 재사용성도 높일 수 있다.
마무리
어쩌다보니 객체지향 5가지 원리 중 마지막인 의존성 역전 원칙을 제일 먼저 작성하게 되었다! 남은 4개라도 순서에 맞춰 올려봐야겠다.
그리고 이러한 DIP 원칙은 "개방/폐쇄 원칙"과 "리스코프 치환 원칙"을 준수하게 된다면 자동적으로 준수된다고 한다.
각각의 원칙은 다른 원칙과 연관성이 있으니 이러한 부분을 잘 이해하고 실전에 적용시켜야할 것 같다.
참고