의존성 역전의 법칙
의존성 역전의 법칙이 뭐야..
객체 지향 프로그래밍에서 의존관계 역전 원칙은 소프트웨어 모듈들을 분리하는 특정 형식을 지칭한다.
이 원칙을 따르면,
상위 계층(정책 결정)이 하위 계층(세부 사항)에 의존하는 전통적인 의존관계를 반전(역전)시킴으로써
상위 계층이 하위 계층의 구현으로부터 독립되게 할 수 있다.
애당초 역전
이라는 워딩이, 익숙하지가 않습니다.
지금이나마 여러 책을 읽고, 의존성 역전
을 이해한 내용을 조금이나마 기록합니다.
<br>
의존성 역전하기
다음 구조의 클래스 다이어그램이 있습니다.
Product
는 상품
으로 판매정책을 가지고있습니다.
CashSalePolicy
는 현금 판매 정책
으로 상품의 현금 가격 할인을 결정합니다.
class Product
class CashSalePolicy
Product -> CashSalePolicy
class Product{
cashSalePolicy
}
class CashSalePolicy{
discount()
}
Product
은 CashSalePolicy
에 의존하고 있습니다.
그 말은 즉 Product
을 사용하는데 있어서, CashSalePolicy
를 필요로 한다는 말이기도 합니다.
그리고 CashSalePolicy
의 변동은 Product
에 영향을 주게 됩니다.
<br/>
그럼 할인 로직이 바뀐다면 어떨까요?
특정 상품은 CashSalePolicy(현금판매정책)
이 아니라,
CardSalePolicy(카드판매정책)
을 필요로 한다면요?
class Product
class CashSalePolicy
Product -> CashSalePolicy
Product -> CardSalePolicy
class Product{
cashSalePolicy
cardSalePolicy
}
class CashSalePolicy{
discount()
}
class CardSalePolicy{
discount()
}
Product
에는 새로운 멤버변수가 추가되었습니다.
이처럼 구체화된, 세부사항에 대한 의존은 코드의 변경을 발생시킵니다.
<br/>
따라서 우리는 추상화
를 사용합니다.
그리고 다음 구조로 추상화를 적용하였습니다.
class Product
interface SalePolicy
class CashSalePolicy
class CardSalePolicy
SalePolicy <|-- CashSalePolicy
SalePolicy <|-- CardSalePolicy
Product -> SalePolicy
class Product{
salePolicy:SalePolicy
}
Product
은 추상화된 SalePolicy(판매정책)
인터페이스에 의존합니다.
하지만 구체화된 CashSalePolicy(현금판매정책)
에는 의존하지 않습니다.
그 이유는 CashSalePolicy(현금판매정책)
의 의존성 방향이 SalePolicy(판매정책)
에 의존하고 있기 때문입니다.
<br/>
여기서 구체화된 세부사항을 하위모듈
, 추상화된 인터페이스를 상위모듈
로 정의합니다.
기존의 절차지향(상품->현금판매정책)과 같이 상위 모듈이 하위모듈을 의존하는게 아닌,
하위 모듈이 상위 모듈을 의존함(상품->판매정책<-현금판매정책) 을 의존성역전
이라 정의합니다.
<br/>
다음 과정을 정리하면 다음과 같습니다.
- 상위수준 모듈은 하위 수준 모듈에 의존하면안된다, 둘 모두 추상화에 의존해야한다.
- 추상화는 구체적인 사항에 의존해서는 안된다.
<br>
그럼 의존성 역전이 왜 중요할까요?
그래! 그럼 의존성방향이 거꾸로 됬네!
추상화를 통해서, 수정에는 닫혀있으며, 확장에는 열려있다!
근데 의존성역전, 근데 왜 이게 중요한거야..?
<br/>
의존성역전을 함으로써 우리는 독립된 모듈을 구현할 수 있습니다.
구체화된 CashSalePolicy
를 없애버리고 일부 다이어그램을 떼어서 보면, 이해하기 쉽습니다.
class Product
interface SalePolicy
Product -> SalePolicy
해당 모듈에서는 CashSalePolicy
라는 존재 자체를 필요로 하지 않습니다.
Product
는 단지 SalePolicy
만 알면 될 뿐입니다.
<br/>
모듈의 외부영역으로 확장해봅니다.
package A{
class Product
interface SalePolicy
}
package B{
class CashSalePolicy
}
package C{
class CardSalePolicy
}
SalePolicy <|-- CardSalePolicy
SalePolicy <|-- CashSalePolicy
Product -> SalePolicy
B
, C
모듈은 각자, A
모듈의 SalePolicy
을 의존하여 구현체를 정의했습니다.
이는 앞으로 D
, E
, F
..... 어느 모듈이 오더라도,
A
모듈의 SalePolicy
에 의존하여, 각자의 로직에 맞추어 세부사항을 구현할 수 있음을 의미합니다.
즉, 우리에겐 어떤 판매정책
이라도 열려있습니다.
누군가 A
라는 모듈을 공유해주면, 자유롭게 판매정책을 만들어 상품을 판매 할 수 있습니다.
<br/>
어떤 판매정책
을 꽂더라도, 동작하는 프로그램을 만들 수 있습니다.
이를 플러그인 아키텍처라 합니다.
의존성역전 법칙은 이런 플러그인 아키텍쳐
를 구현가능하게 합니다.
<br/>
책 클린아키텍쳐
에서는 객체지향 패러다임
의 의존성역전을 강조합니다.
객체지향이란 무엇인가?
소프트웨어 아키텍처 관점에서는 명확하다.
객체지향이란 다형성을 이용하여 전체 시스템의 모든 소스코드 의존성에 대한 제어권한을 흭득할 수 있는 능력이다.
클린아키텍처에서 객체지향 패러다임의 지향점은 다형성
에 있다고 합니다.
다형성
을 통해, 의존성을 우리가 의도하는 바로 역전시킬 수 있습니다,
위의 모습과 같이 독립된 모듈을 구현가능함을 강조합니다.
<br/>
그럼 우리는 더 나아가 제어의 역전
까지 확장하여 이해할 수 있습니다.
자세히 생각해보면, 이 모습을 우리는 어디서 많이 봤습니다.
바로 프레임워크
를 사용할 때 입니다.
<br/>
프레임워크는 수 많은 추상화된 인터페이스를 제공합니다.
그리고 우리는 인터페이스를 구현하여, 우리가 원하는 세부 로직을 마음껏 작성합니다.
프레임워크가 확장이 열려있기를 원하는 곳에 의존성을 역전시켰기 때문입니다.
따라서 어느 구현체에도 열려있으며, 우리는 구현체를 자유롭게 작성 할 수있습니다.
<br/>
프로그램 실행하면, 프레임워크는 추상화된 상위 모듈의 코드를 진행하며 세부화된 우리의 코드를 실행합니다.
즉, 코드를 실행하는 주체가 프레임워크가 된것입니다.
이를 곧 제어의 역전
이라 정의합니다.
<br/>
다음은 책 오브젝트
에 프레임워크에 관련된 일부 문장입니다.
프레임워크는 추상화를 통해 상위 모듈을 구현하였고,
우리는 상위 모듈에 의존하는 하위 모듈을 구현하게 된다.예를 들어 상속을 받아 하위 모듈인 구현체를 작성 할 수도 있을 것이다.
결국 프레임워크의 상위 모듈은 우리가 작성한 하위모듈을 호출한다.
프레임워크가 어플리케이션에 속하는 서브 클래스들을 호출하게 되는 것이다.
이를 곧 제어의 역전 이라 한다.
<br/>
결론
결론, 의존성역전은 의존성이 기존의 절차지향과 거꾸로 됬음을 말합니다.
의존성역전을 통해 우리는 의존관계를 우리의 맘대로 바꿀 수가 있습니다.
우리에게 의존성을 컨트롤할 권한을 부여해줍니다.
의존성역전을 이용하면, 우리가 확장을 의도하고자 한 위치에
확장에 열려있는 구조를 만들수 있습니다.
클린아키텍처에서는 이를 강조하여 객체지향 패러다임의 지향점을 다형성에 찾습니다.
오브젝트의 의존성역전은 프레임워크를 넘어 IoC개념과 연장됨을 말해줍니다.