DI, AppConfig

현재·2024년 1월 27일

작성 코드 문제점

package hello.core.order;

import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixedDiscountPolicy;
import hello.core.member.*;

public class OrderServiceImpl implements OrderService{
    private final MemberRepository memberRepository = new MemoryMemberRepository();
   
   private final DiscountPolicy discountPolicy = new FixedDiscountPolicy();
   
   @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member,itemPrice);
        return new Order(member.getId(),itemName,itemPrice,discountPrice);
    }
}

코드의 문제점

  • 초기 확정된 할인정책이 없어서 설정해둔 고정 금액 할인 방식을 이 후 정률 할인정책으로 변경하려 하는 상황.
  • 방식을 변경하려면 아래 코드 부분을 수정하여야 하므로 OCP를 위반
 private final DiscountPolicy discountPolicy = new FixedDiscountPolicy();
 -> private final DiscountPolicy discountPolicy = new RatedDiscountPolicy();
  • 위의 코드 내용을 살펴보면 주문서비스 클라이언트(OrderServiceImpl)은 DiscountPolicy 인터페이스 뿐만 아니라 구체 클래스(RatedDiscountPolicy)에도 의존하고 있다. -> DIP 위반
  • 추상(인터페이스)에만 의존하도록 변경
    기대했던 의존관계

    실제 의존관계

해결방안

public class OrderServiceImpl implements OrderService {
	//private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
    private DiscountPolicy discountPolicy;
  • 인터페이스에만 의존하도록 설계 변경
  • 하지만 구현체가 없어 NPE(Null Point Exception) 발생.

AppConfig

  • 애플리케이션의 전체 동작 방식을 구성(config)하기 위해, 구현 객체를 생성하고 연결하는 책임을 가지는 별도의 설정 클래스
public class AppConfig {

	public MemberService memberService() {
    	return new MemberServiceImpl(new MemoryMemberRepository);
        }
 
 	public OrderService orderService() {
    	return new OrderServiceImpl(
        	new MemoryMemberRepository(),
            new RatedDiscountPolicy());
        }
 }
  • MemberServiceImpl
public class MemberServiceImpl implements MemberService {
	private final MemberRepository memberRepository;
    
    public MemberServiceImpl(MemberRepository memberRepository) {
    	this.memberRepository = memberRepository;
        }
        
        ...
        
}
  • OrderServiceImpl
public class OrderServiceImpl implements OrderService {
	private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
    
    public OrderServiceImpl orderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
    	this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
        }
        
       ...
       
}
  • AppConfig는 애플리케이션의 실제 동작에 필요한 구현 객체를 생성
    - MemberServiceImpl, MemoryMemberRepository, OrderServiceImpl, FixedDiscountPolicy

  • AppConfig는 생성한 객체 인스턴스의 참조(레퍼런스)를 생성자를 통해서 주입
    - MemberServiceImpl -> MemoryMemberRepository,
    OrderServiceImpl -> MemoryMemberRepository, FixedDiscountPolicy

  • 생성자 주입을 통해서 MemberServiceImpl은 MemberRepository 인터페이스만 의존. MemberServiceImpl 입장에서 어떤 구현 객체가 들어올지 알수 없고 MemberServiceImpl의 생성자를 통해서 어떤 구현 객체를 주입할지는 외부(AppConfig)에서 결정된다.
    -> MemberServiceImpl은 의존관계에 대한 고민 여부는 외부에 맡기고 실행에만 집중하게 된다.

  • 객체 생성과 연결은 AppConfig가 담당.
  • DIP 완성: MemberServiceImpl은 MemberRepository인 추상에만 의존. 구체 클래스 몰라도 됨
  • 관심사 분리: 객체를 생성하고 연결하는 역할과 실행하는 역할이 명확히 분리

  • appConfig 객체는 memoryMemberRepository를 생성하고 그 참조값을 memberServiceImpl을 생성하면서 생성자로 전달.
  • 클라이언트인 memberServiceImpl 입장에서 보면 의존관계를 마치 외부에서 주입해주는 것 같다고 해서 DI(Dependency Injection) 우리말로 의존관계 주입이라 한다.

0개의 댓글