logo

의존성 역전의 법칙이 뭐야..

객체 지향 프로그래밍에서 의존관계 역전 원칙은 소프트웨어 모듈들을 분리하는 특정 형식을 지칭한다.
이 원칙을 따르면,
상위 계층(정책 결정)이 하위 계층(세부 사항)에 의존하는 전통적인 의존관계를 반전(역전)시킴으로써
상위 계층이 하위 계층의 구현으로부터 독립되게 할 수 있다.

애당초 역전이라는 워딩이, 익숙하지가 않습니다.
지금이나마 여러 책을 읽고, 의존성 역전을 이해한 내용을 조금이나마 기록합니다.

<br>

의존성 역전하기

다음 구조의 클래스 다이어그램이 있습니다.

Product상품으로 판매정책을 가지고있습니다.
CashSalePolicy현금 판매 정책으로 상품의 현금 가격 할인을 결정합니다.

class Product
class CashSalePolicy
Product -> CashSalePolicy

class Product{
    cashSalePolicy
}

class CashSalePolicy{
    discount()
}

ProductCashSalePolicy의존하고 있습니다.
그 말은 즉 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개념과 연장됨을 말해줍니다.

CommentCount 0
이전 댓글 보기
등록
TOP