woowacourse-study / 2022-object-study

우테코 4기 오브젝트 스터디
3 stars 4 forks source link

메소드 주입은 의존성 주입의 한 종류로 볼것인가 ? #67

Open cjlee38 opened 2 years ago

cjlee38 commented 2 years ago

주제

메소드 주입은 의존성 주입의 한 종류로 볼것인가 ?

선정 이유

책에서는 메소드 주입은 의존성 주입의 한 종류로 볼것인가에 대해서는 논란의 여지가 있다. 라고 이야기했지만, 이에 대해서는 자세히 설명해주지 않았는데요. 개인적으로 메소드로 파라미터로 넘겨주는 것엔 단순히 '일부 메소드에서만 사용되기 때문인가?' 라는 의문을 품어왔기 때문에, 이와 관련해서 한번 찾아보고, 또 개인적인 생각을 정리해보고자 합니다.

생성자든, setter든, 메소드든, 결국 의존성을 주입받는 클래스 입장, 즉 컴파일 타임 의존성에서는 어떤 것을 선택하더라도 상관이 없습니다. 하지만 문제는 런타임 의존성인데요. 생성자는 런타임 의존성을 '초기에' 설정해줄 수 있습니다. 이와 관련해서 찾아보니 'Composition Root' 라는 개념이 있었는데요. 아주 간략하게, 어플리케이션의 entry point에 가깝게 위치하면서, 모든 객체의 의존성을 조립하는 개념으로 이해했습니다.(A Composition Root is a (preferably) unique location in an application where modules are composed together.)

다음은 screening 에 대한 Composition Root의 예시입니다.

class EntryPoint {
    public static void main(String[] args) {
        Screening screening = new Screening(
            new Movie(
                new AmountDiscountPolicy(
                    List.of(
                        new PeriodDiscountCondition(),
                        new SequenceDiscountCondition()
                    )
                )
            )
        );
    }
}

또한, 각각의 new 생성자를 별도의 메소드와 변수로 분리하게되면 조금 억지스럽긴해도 setter를 사용할 수 있습니다. 하지만, 메소드 주입의 경우 이러한 조립이 불가능합니다. 만약, DiscountPolicy의 조건으로 Period와 Sequence를 메소드를 통해 넘겨준다고 가정해보겠습니다. 이러한 경우엔, 내부 깊숙이 숨겨져 있는 메소드를 호출하는 그 순간이 와야 비로소 어떤 구체클래스를 넣어줘야할지 결정할 수 있고, 그 주체는 DiscountPolicy에게 요청하는 Movie가 되기 때문입니다. 즉, Movie가 PeriodDiscountCondition이라는 구체 클래스에 직접 의존하는 상황이 됩니다.

그렇다면 Movie의 생성자로 DiscountCondition을 넣어주면 해결될까요? Movie가 추상적인 DiscountCondition에 의존한다고 가정해본다면, 할인 조건을 판단하는 방법은 두 가지가 있습니다.

  1. Movie가 DiscountPolicy에게 DiscountCondition을 넘겨주는 방법
  2. Movie가 DiscountCondition 을 호출해서 할인 여부를 확인한 후, 할인이 가능할 경우 DiscountPolicy로부터 할인 금액을 받는 방법.

결론부터 말하면 두 가지 모두 딱히 좋은 선택으로는 보이지 않습니다.

왜냐하면, 1번을 선택하는 경우에는 Movie 입장에서는 굳이 본인에게 필요하지 않은 DiscountCondition에 대한 의존성을 가지게 됩니다. 즉, LOW COUPLING 패턴(5장, 책임 할당하기, p143)에 어긋나게 됩니다. 반대로 2번을 선택하는 경우는 Movie가 DiscountCondition과 DiscountPolicy 두 곳에 의존하고 있기 때문에, 둘 중 한곳에 변경이 생긴다면 그에 영향을 받게 됩니다.

하지만 메소드 파라미터가 반드시 필요한 경우도 있는데, 바로 런타임중에 값이 시시각각 변하는(즉, 생성되는) 객체를 핸들링 하는 경우라고 생각합니다. 가장 대표적인 예가 '값객체' 인데요. (Screening, Movie 등도 하나의 값으로 볼 수 있지 않느냐? 라고 생각할 수 있지만, 개인적으로는 "영화 예매를 위한 금액 계산기"에 조금 더 가깝다고 생각합니다. Reservation이 이 예시에 해당할 것 같네요)

이 때는 객체의 생성 자체가 서로 다른 요청에 의해 생성되므로, 메소드 주입을 해주거나, 런타임 중에 특정 객체 내부에서 new로 생성될 수밖에 없을 것으로 보입니다.

(참고: https://freecontent.manning.com/dependency-injection-in-net-2nd-edition-understanding-the-composition-root/)

해당 텍스트

따라서 의존성 주입에는 의존성을 해결하는 세 가지 방법을 가리키는 별도의 용어를 정의한다.

메소드 주입을 의존성 주입의 한 종류로 볼 것인가에 대해서는 논란의 여지가 있다. 개인적으로는 외부에서 객체가 필요로 하는 의존성을 해결한다는 측면에서 의존성 주입의 한 종류로 간주한다.

관련 페이지

p.293, 294