Framework/Spring

인프런 스프링 핵심 원리-기본편 #3 스프링 핵심원리 2 - 객체 지향 원리 적용

MINGYUM 2022. 1. 3. 00:22

AppConfig 클래스를 hello.core 디렉터리의 하위에 넣는다. 

public class AppConfig {
    public MemberService memberService(){
        return new MemberServiceImpl(new MemoryMemberRepository());
    }
}

이렇게 MemberService() 함수를 정의해를 해주고, MemoryMemberRepository()를 매개변수로 한 MemberServiceImpl 객체를 반환한다. 

 

이는 다음과 같은 코딩 방식을, 

이렇게 바꿔주는 것이 가능하도록 한다. 

 

이 코드는 인터페이스만 확인할 수 있으며 구현체에 전혀 의존하지 않는 코드가 되는 것이다. 

즉 AppConfig는 생성한 객체 인스턴스의 참조를, 생성자를 통해서 주입 해준다.

 

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

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

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

실제 실행할 때는, 구현체를 지정해 줄 필요 없이 단순히 AppConfig에서 memberService 함수를 호출하면

자동으로 MemberServiceImpl가 반환되어 memberService에 담기게 된다. 

 

public class MemberServiceTest {

    MemberService memberService;

    @BeforeEach
    public void beforeEach(){
        AppConfig appConfig = new AppConfig();
        memberService = appConfig.memberService();
    }

    @Test
    void join(){
        //given
        Member member = new Member(1L, "memberA", Grade.VIP);

        //when
        memberService.join(member);
        Member findMember = memberService.findMember(1L);

        //then
        Assertions.assertThat(member).isEqualTo(findMember);
    }
}

 

테스트 코드에서도, 

이렇게 @BeforeEach 라는 어노테이션으로 AppConfig 객체를 먼저 생성해주고

memberService에 대한 구현체를 만들어준다. 

그리고 실제 테스트 함수에서, Member 객체를 통해 Join과 findMember을 실행하면 완성 !!

 

OrderServiceImpl에 memberRepository, disCountPolicy를 이렇게 생성해서 다형성을 구현할 수 있지만,

밑에 처럼 내부 함수를 이용해 중복을 제거할 수 있다. 

package hello.core;

import hello.core.discount.DiscountPolicy;
import hello.core.discount.FixDiscountPolicy;
import hello.core.discount.RateDiscountPolicy;
import hello.core.member.MemberRepository;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;

public class AppConfig {
    public MemberService memberService(){
        return new MemberServiceImpl(new MemoryMemberRepository());
    }
    private MemberRepository memberRepository(){
        return new MemoryMemberRepository();
    }
    public OrderService orderService(){
        return new OrderServiceImpl(memberRepository(), disCountPolicy());
    }

    public DiscountPolicy disCountPolicy(){
//        return new FixDiscountPolicy();
        return new RateDiscountPolicy();
    }
}

 

최종 AppConfig 코드

 

OCP : 할인 요소가 변경되었을 경우, RateDisCountPolicy로 AppConfig에서만 변경되므로 

클라이언트 코드는 변경할 필요가 없다. 

 

 

IoC (Inversion of Control)

: 제어의 역전

 

기존의 프로그램은 클라이언트 측에서 객체가 필요한 요구사항에 맞춰 여러 방면에서 바꿔주어야 했었는데,

변경 이후 AppConfig 의 인스턴스 객체만 바꿔주면 해결 가능해졌다. 

 

동적인 객체 인스턴스 의존 관계

이렇게 경우에 따라, AppConfig를 바꿔서 실제 실행 시 appConfig 객체의 멤버 함수를 호출할 때 어떤 객체가 new 되어 반환되는 지 결정할 수 있다.

의존 관계 주입을 통해, 클라이언트 코드 변경 없이 인스턴스를 변경할 수 있게 되었다

이러한 컨테이너 구조를 DI 컨테이너라고 한다.

 

스프링으로 전환하기

 

1.     AppConfig

@Configuration 클래스 상단에 붙이기

@Bean을 함수마다 붙이기

Application 생성 시 환경

밑의 getBean 함수에서 memberService라는 함수를 호출하여 MemberService 객체를 얻어온다.

 

이제, 스프링 컨테이너에 스프링 빈을 등록하고, 컨테이너에서 스프링 빈을 찾아서 사용하도록 변경되었다.

ApplicationContext는 인터페이스, AnnotationApplicationContext는 구현체이다.