인프런 스프링 핵심 원리-기본편 #3 스프링 핵심원리 2 - 객체 지향 원리 적용
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는 구현체이다.