본문 바로가기

Spring

Spring에서 순수 Java코드로 의존관계 주입해보기 (OCP,DIP 문제를 어떻게 해결하지?)

원리를 모르고 기능만 사용해서 결과물 내기에만 급급했었다.

이제는 결과물을 내는 것도 중요하지만, 어떻게 작동하는지에 대해서 이해가 필요하다고 생각든다.

 

Spring의 기능을 사용하지 않고 순수 Java코드로 DI컨테이너를 구현해봤다.

앞으로는 Spring으로 전환하여 각각의 어노테이션들이 어떤 의미를 내포하고 있는지 알아볼 계획이다.

 

먼저, 다형성을 활용해서 코드를 작성했다.

public class OrderServiceImpl implements OrderService {

    private final MemberRepository memberRepository = new MemoryMemberRepository();
    private final DiscountPolicy discountPolicy = new FixDiscountPolicy();

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member, itemPrice);

        return new Order(memberId, itemName, itemPrice, discountPrice);
    }
}

그러나 위 코드는 문제가 있다.

 

1. 현재 orderServiceImpld은 DiscountPolicy라는 interface에 의존하고있고, 동시에 구현체인 FixDiscountPolicy에도 의존하고 있다.

추상(interface)뿐만 아니라 구체클래스에도 의존하고 있기 때문에 DIP 원칙을 지키지 못하고 있다.

2.  DiscountPolicy의 구현체를 변경하려면 클라이언트인 OrderServiceImpl 코드를 고쳐야 한다. 이는 OCP 원칙을 위반하고 있다. OCP는 확장에는 열려있지만, 수정에는 닫혀있어야 한다는 뜻이다. 

*OCP, DIP : SOLID 원칙 중 O, D

 

위 두가지 문제점을 해결하기 위해서 외부의 클래스에서 OrderServiceImpl에 DiscountPolicy의 구현체를 대신 생성하고 주입해줘야 한다.

이를 위해 AppConfig라는 클래스를 생성했다.

public class AppConfig {

    public MemberService memberService() {
        return new MemberServiceImpl(memberRepository());
    }

    private MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }

    public OrderService orderService() {
        return new OrderServiceImpl(memberRepository(), discountPolicy());
    }

    public DiscountPolicy discountPolicy() {
        return new RateDiscountPolicy();
    }
}

이제는, AppConfig에서 구체 클래스를 선택하고 주입한다. 애플리케이션이 어떻게 동작해야 할 지, 전체 구성을 책임지는 클래스다.

 

AppConfig에서 구현체를 생성해주기 때문에 OrderServiceImpl에서는 더이상 구현체를 생성하지 않아도 된다.

 

그리고 아래 코드처럼 변경해주면 AppConfig를 통해서 구현체를 주입받을 수 있다.

public class OrderServiceImpl implements OrderService {

    private MemberRepository memberRepository;
    private DiscountPolicy discountPolicy;

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

    @Override
    public Order createOrder(Long memberId, String itemName, int itemPrice) {
        Member member = memberRepository.findById(memberId);
        int discountPrice = discountPolicy.discount(member, itemPrice);

        return new Order(memberId, itemName, itemPrice, discountPrice);
    }
}

각각의 클래스는 담당 기능만 실행하는 책임만 가지면 된다.

AppConfig가 있기 전, OrderServiceImpl은 구현체를 생성하는 책임도 동시에 가지고 있었지만,

이제는 기능을 실행하는 책임만 지면 된다.

 

이처럼 AppConfig의 역할(객체를 생성하고 관리하면서 의존관계를 연결)해주는 것을 IoC 컨테이너 또는 DI컨테이너라고 한다.

 

순수 Java코드만으로 의존성 주입을 구현해봤다.

이후로는 Spring의 기능을 사용해서 DI컨테이너를 만들고, 이에 따른 장점들을 알아볼 예정이다.

 

 

스프링 핵심 원리 - 기본편 - 인프런 | 강의

스프링 입문자가 예제를 만들어가면서 스프링의 핵심 원리를 이해하고, 스프링 기본기를 확실히 다질 수 있습니다., - 강의 소개 | 인프런...

www.inflearn.com