오봉이와 함께하는 개발 블로그
Spring - 스프링 핵심 원리 이해2 - 2(객체 지향 원리 적용) 본문
728x90
관심사의 분리
- 본인의 역할을 수행하는 것에만 집중해야 한다
- 어떤 구현체가 선택되더라도 코드 변경 없이 실행할 수 있어야 한다.
- 이를 위해 중간에 연결할 수 있는 무언가가 필요하다!
AppConfig 등장
- 앱의 전체 동작 방식을 구성(config)하기 위해 구현 객체를 생성하고, 연결하는 책임을 가지는 별도의 설정 클래스
package hello.core;
import hello.core.discount.FixDiscountPolicy;
import hello.core.member.MemberSerivce;
import hello.core.member.MemberServiceImpl;
import hello.core.member.MemoryMemberRepository;
import hello.core.order.OrderService;
import hello.core.order.OrderServiceImpl;
public class AppConfig {
public MemberSerivce memberSerivce() {
return new MemberServiceImpl(new MemoryMemberRepository());
}
public OrderService orderService() {
return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
}
}
- AppConfig는 앱의 실제 동작에 필요한 구현 객체 생성
- MemberSerivce (인터페이스)
- MemberServiceImpl (구현체)
- MemoryMemberRepository
- MemberServiceImpl (구현체)
- OrderService (인터페이스)
- OrderServiceImpl (구현체)
- MemoryMemberRepository
- FixDiscountPolicy
- OrderServiceImpl (구현체)
- MemberSerivce (인터페이스)
생성자 주입
package hello.core.member;
public class MemberServiceImpl implements MemberSerivce{
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
/*
...
*/
}
- AppConfig로 설계를 변경했기 때문에 MemberServiceImpl은 MemoryMemberRepository를 의존하지 않음
- MemberRepository(인터페이스)만 의존
- MemberServiceImpl는 어떤 구현체가 들어올지 알 수 없음
- MemberServiceImpl의 생성자를 통해 어떤 구현체가 주입될지는 외부(AppConfig)에서 결정된다
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
/*
...
*/
}
- AppConfig로 설계를 변경했기 때문에 OrderServiceImpl은 MemoryMemberRepository, FixDiscountPolicy를 의존하지 않음
- MemberRepository, DiscountPolicy(인터페이스)만 의존
- OrderServiceImpl은 어떤 구현체가 들어올지 알 수 없음
- OrderServiceImpl의 생성자를 통해 어떤 구현체가 주입될지는 외부(AppConfig)에서 결정된다
클래스 다이어그램
- 객체 생성과 연결은 AppConfig가 담당
- DIP 완성
- MemberServiceImpl은 MemberRepository인 추상에만 의존하면 됨(구체 클래스를 몰라도 된다)
- 이로써 객체를 생성하고 연결하는 역할과 실행하는 역할이 명확히 분리됨
회원 객체 인스턴스 다이어그램
- AppConfig는 MemoryMemberRepository객체를 생성하고 참조값을 MemberServiceImpl을 생성하며 참조값 전달
- 클라이언트(MemberServiceImpl)의 입장에서 보면 의존관계를 외부(AppConfig)에서 주입하는 거 같다 하여 DI(Dependency Injection), 의존성 주입이라 함
AppConfig 실행
public class MemberApp {
public static void main(String[] args) {
AppConfig appConfig = new AppConfig();
MemberSerivce memberSerivce = appConfig.memberSerivce();
// MemberSerivce memberSerivce = new MemberServiceImpl();
Member member = new Member(1L, "memberA", Grade.VIP);
memberSerivce.join(member);
Member findMember = memberSerivce.findMember(1L);
System.out.println("new member = " + member.getName());
System.out.println("find member = " + findMember.getName());
}
}
public class OrderApp {
public static void main(String[] args) {
AppConfig appConfig = new AppConfig();
MemberSerivce memberSerivce = appConfig.memberSerivce();
OrderService orderService = appConfig.orderService();
// MemberSerivce memberSerivce = new MemberServiceImpl();
// OrderService orderService = new OrderServiceImpl();
Long memberId = 1L;
Member member = new Member(memberId, "memberA", Grade.VIP);
memberSerivce.join(member);
Order order = orderService.createOrder(memberId, "itemA", 10000);
System.out.println("order = " + order);
System.out.println("order.calculatePrice = " + order.calculatePrice());
}
}
테스트 코드 수정
- @BeforeEach는 각 테스트를 실행하기 전에 호출된다.
public class MemberServiceTest {
MemberSerivce memberSerivce;
@BeforeEach
public void beforeEach() {
AppConfig appConfig = new AppConfig();
memberSerivce = appConfig.memberSerivce();
}
// MemberSerivce memberSerivce = new MemberServiceImpl();
@Test
void join() {
// given
Member member = new Member(1L, "memberA", Grade.VIP);
// when
memberSerivce.join(member);
Member findMember = memberSerivce.findMember(1L);
// then
Assertions.assertThat(member).isEqualTo(findMember);
}
}
public class OrderServiceTest {
MemberSerivce memberSerivce;
OrderService orderService;
@BeforeEach
public void beforeEach() {
AppConfig appConfig = new AppConfig();
memberSerivce = appConfig.memberSerivce();
orderService = appConfig.orderService();
}
// MemberSerivce memberSerivce = new MemberServiceImpl();
// OrderService orderService = new OrderServiceImpl();
@Test
void createOrder() {
Long memberId = 1L;
Member member = new Member(memberId, "memberA", Grade.VIP);
memberSerivce.join(member);
Order order = orderService.createOrder(memberId, "itemA", 10000);
Assertions.assertThat(order.getDiscountPrice()).isEqualTo(1000);
}
}
정리
- AppConfig를 통해 관심사를 확실하게 분리
- 배역, 배우를 생각하자
- AppConfig는 공연 기획자
- AppConfig는 구체 클래스를 선택한다.
- 앱이 어떻게 동작해야 할지 전체 구성을 책임진다
- OrderServiceImpl(MemberService)은 기능을 실행하는 책임만 지면 된다.
출처 : 인프런 김영한 지식공유자님의 스프링 완전 정복 로드맵 강의
728x90
'BE > Spring' 카테고리의 다른 글
Spring - 스프링 핵심 원리 이해2 - 4(객체 지향 원리 적용) (0) | 2022.06.04 |
---|---|
Spring - 스프링 핵심 원리 이해2 - 3(객체 지향 원리 적용) (0) | 2022.06.03 |
Spring - 스프링 핵심 원리 이해2 - 1(객체 지향 원리 적용) (0) | 2022.05.31 |
Spring - 이해를 위해 순수 Java로 작성하는 예제(3)(스프링 핵심 원리 이해1 예제 만들기) (0) | 2022.05.31 |
Spring - 이해를 위해 순수 Java로 작성하는 예제(2)(스프링 핵심 원리 이해1 예제 만들기) (0) | 2022.05.30 |
Comments