티스토리 뷰

DI(Dependency Injection)

DI란 스프링이 제공하는 의존 관계 주입 기능으로, 객체를 직접 생성하는 것이 아닌 외부 APP에서 생성한 후 객체를 주입 시켜주는 방식이다. DI를 통해 모듈 간의 결합도(Coupling)이 낮아지고 유연성이 높아진다.

 

좋은 소프트웨어를 설계하기 위해서는 응집도(Cohesion)는 높아야 하고 결합도(Coupling)은 낮아야 한다.

 

방법 1은 A 객체가 B와 C객체를 직접 생성자를 통해서 생성하는 방법이고

방법 2는 외부에서 생성된 객체를 setter()나 생성자를 통해 사용하는 방법이다

 

DI를 위반하는 클래스 다이어그램 -> 인터페이스와 구현 모두 의존

주문 시스템의 클래스 다이어그램을 살펴보면 문제점을 찾을 수 있다.

할인 정책을 고정 할인 정책에서 비율 할인 정책으로 변경하는 경우 클라이언트 코드인 OrderServiceImpl 클래스에서 할인 정책을 변경해야 한다.

-> 추상(인터페이스) 뿐만 아니라 구체(구현) 클래스에도 의존하고 있다.

즉 방법 1의 방식으로 구현되어 있는 것이다.

 

반드시 추상에만 의존하도록 변경해야 된다! (인터페이스에만 의존)

public class OrderServiceImpl implements OrderService{

     private final MemberRepository memberRepository = new MemoryMemberRepository();

    /**
     * 고정 할인 정책 -> 비율 할인 정책으로 할인 정책을 변경하는 경우
     * 클라이언트 코드인 OrderServiceImpl은 DiscountPolicy 인터페이스 뿐만 아니라 구현 클래스도 함께 의존
     * 그래서 구현 클래스(고정, 비율)를 변경할 때, OrderServiceImpl도 같이 변경해야 된다
     * DIP 위반이므로 추상(Interface)에만 의존하도록 변경해야된다
    */
     private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
     private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
   }

OrderServiceImpl 클래스에서 private final DiscountPolicy discountPolicy = new FixDiscountPolicy(); 를 통해 의존관계를 주입하고 있다. 만약 비율 할인 정책으로 바꾸게 된다면 OrderServiceImpl 클래스 에서 로직을 수정해야 한다.

-> DI 위반

DI를 지키는 클래스 다이어그램 -> 인터페이스에만 의존

public class AppConfig {

    /**
     * 애플리케이션에 관한 모든 환경 구성은 AppConfig에서 한다
     * AppConfig 에서 앱과 관련된 구현 객체를 생성자 주입의 방식으로 설정한다
     *
     * @return
     */
    public MemberService memberService(){
        return new MemberServiceImpl(new MemoryMemberRepository());
    }

    public OrderService orderService(){
        return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
    }
}

따라서 애플리케이션에 관한 모든 환경 구성을 하는 AppConfig 클래스를 정의한다.

AppConfig 클래스에서 앱과 관련한 구현 객체를 생성자 주입 방식으로 설정하고

public class OrderServiceImpl implements OrderService{

    /**
     * 그래서 생성자 주입의 방식으로 정의한다
     * 따라서 MemberRepository, DiscountPolicy 와 같은 인터페이스에만 의존한다
     */
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;

    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }

OrderServiceImpl 코드를 다음과 같이 생성자를 이용하여 DI를 주입하는 방식을 사용한다.

 

public class MemberApp {

    public static void main(String[] args) {
        AppConfig appConfig = new AppConfig();
        MemberService memberService = appConfig.memberService();

        Member member = new Member(1L, "memberA", Grade.VIP);
        memberService.join(member);

        Member findMember = memberService.findMember(1L);
        System.out.println("findMember = " + findMember.getName());
        System.out.println("member = " + member.getName());
    }
}

애플리케이션 내부에서는 사용할 때 AppConfig 객체를 만들어 할당해주면 된다.

댓글