관심사의 분리
애플리케이션을 하나의 공연이라 생각해보자. 로미오와 줄리엣 공연을 한다고 가정하면 로미오 역할을 누가할지 줄리엣 역할을 누가할지 배역을 선택하는 것은 누구일까? 배역을 정한느 것은 배우가 아니다. 배우가 배역을 정한다면 배우는 너무 많은 책임을 가지게 된다.
"관심사를 분리하자"
- 배우는 본인의 역할인 배역을 수행하는 것에만 집중해야 한다.
- 배우는 어떤 상대 배우가 선택되더라도 똑같이 공연을 할 수 있어야 한다.
- 공연을 구성하고, 배우를 섭외하고, 역할에 맞는 배우를 지정하는 책임은 별도의 "공연 기획자"가 담당한다.
- 공연 기획자를 만들고, 배우와 기획자의 책임을 분리하자.
AppConfig 의 등장
애플리케이션의 전체 동작 방식을 구성하기 위해 "구현 객체를 생성"하고 "연결"하는 책임을 가지는 별도의 설정 클래스를 만들어 보자.
hello.core 패키지에 AppConfig 파일 생성.
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(new MemoryMemberRepository());
}
}
=> 이전에는 객체를 생성하고 할당하는 일을 MemberService에서 했었다. 이제 이 역할을 AppConfig에서 할 것이다.
MemberServiceImple 에서 객체 생성하는 부분 제거하고 생성자를 만든다.
package hello.core.member;
public class MemberServiceImpl implements MemberService{
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
@Override
public void join(Member member) {
memberRepository.save(member);
}
@Override
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
}
=> MemberServiceImpl에는 이제 MemoryMemberRepoitory에 대한 코드는 제거 되고, memberRepository만 남아있다.
추상화에만 의존하면서 DI의 원칙을 지키게 된다.
생성자를 통해서 객체가 주입되는 것을 생상자 주입이라 한다.
orderServiceImpl도 계속해서 수정해보자.
AppConfig.java
package hello.core;
import hello.core.discount.FixDiscountPolicy;
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());
}
public OrderService orderService() {
return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
}
}
OrderServiceImpl.java
package hello.core.order;
import hello.core.discount.DiscountPolicy;
import hello.core.member.Member;
import hello.core.member.MemberRepository;
public class OrderServiceImpl implements OrderService{
private final 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);
}
}
MemberApp.java 수정
public class MemberApp {
public static void main(String[] args) {
AppConfig appConfig = new AppConfig();
MemberService memberService = appConfig.memberService();
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("findMember: " + findMember.getName());
}
}
=> 멤버 서비스 생성하는 부분 Appconfig를 통해 생성자 주입하도록 변경
테스트 코드 수정
package hello.core.member;
import hello.core.AppConfig;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
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를 통해 생성자 주입
package hello.core.order;
import hello.core.AppConfig;
import hello.core.member.Grade;
import hello.core.member.Member;
import hello.core.member.MemberService;
import hello.core.member.MemberServiceImpl;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class OrderServiceTest {
MemberService memberService;
OrderService orderService;
@BeforeEach
public void beforeEach() {
AppConfig appConfig = new AppConfig();
memberService = appConfig.memberService();
orderService = appConfig.orderService();
}
@Test
void createOrder() {
Long memberId = 1L;
Member member = new Member(memberId, "memberA", Grade.VIP);
memberService.join(member);
Order order = orderService.createOrder(memberId, "itemA", 10000);
Assertions.assertThat(order.getDiscountPrice()).isEqualTo(1000);
}
}
AppConfig
- AppConfig는 애플리케이션의 실제 동작에 필요한 "구현 객체를 생성" 한다.
- AppConfig는 생성한 객체 인스턴스의 참조를 통해 "생성자를 통해서 주입" 해준다.
=> 객체의 생성과 연결 담당.
관심사의 분리
DIP의 완성
* 인프런 '스프링 핵심 원리 -기본편' 강의를 참고하여 작성했습니다.
'웹 개발 > Spring' 카테고리의 다른 글
[Spring 기본] 새로운 구조와 할인 정책 적용 (0) | 2024.08.13 |
---|---|
[Spring 기본] AppConfig 리팩토링 (0) | 2024.08.13 |
[Spring 기본] 새로운 할인 정책 개발 (0) | 2024.08.12 |
[Spring 기본] 주문과 할인 도메인 개발 (0) | 2024.08.08 |
[Spring 기본] 주문과 할인 도메인 설계 (0) | 2024.08.08 |