Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
Tags
- transaction
- jpa
- 백준
- Servlet
- QueryDSL
- 알고리즘
- 그리디
- 스프링 핵심 원리
- 김영한
- springdatajpa
- AOP
- SpringBoot
- 스프링 핵심 기능
- 스프링
- Proxy
- pointcut
- Thymeleaf
- JDBC
- JPQL
- kotlin
- Android
- 자바
- Exception
- Spring Boot
- 인프런
- spring
- java
- http
- Greedy
- db
Archives
- Today
- Total
개발자되기 프로젝트
관심사의 분리 본문
- 애플리케이션은 하나의 공연
- 인터페이스 = 역할(배역)
- 그럼 배우 선택은 누가?? 배우선택은 배우가 하지 않는다.
- 클라이언트가 인터페이스와 구현체 모두 의존했다.
인터페이스 = new 구현체() - 즉, 다른 배우(클라이언트)가 다른 배역의 배우를 선택한 것과 같다.
- 이상하지않음? 배우는 다른 배우가 정할 수 없고 기획자나 감독이 지정해야한다.
- 이전 코드를 다시 보면 배우의 역할에 배우 선택까지 포함이 되었던 것이다.
- 관심사 분리가 되지 않았던 것이다.
- 따라서 기획자를 만들어 배우, 감독의 책임을 화아아악실히 분리해야 한다.
- 즉! 어떤 구현체를 사용할지 정하는 것은 클리이언트에서 하지 않는다!
1. 그럼 누가 구현체를 정하는데? AppConfig
- 애플리케이션의 전체 동작 방식을 구성(config)하기 위해
- "구현 객체를 생성"하고 "연결"하는 "책임"을 가지는 별도의 설정 클래스가 필요하다.(스프링이 하던 역할...?)
2. MemberServviceImpl 수정
- MemberRepository도 클라이언트(MemberServiceImpl)에서 구현체를 선택했다.
- 생성자를 통해 구현체를 주입받도록 변경하자.
- 자 이제 MemberServiceImpl은 DIP, OCP를 모두 만족한다.
- DIP : 추상화에만 의존
- OCP : 추상화에만 의존 하기 때문에 구현체를 변경해도 클라이언트에는 코드변경 없음.
public class MemberServiceImpl implements MemberService{ //private final MemberRepository memberRepository ; private MemberRepository memberRepository ; public MemberServiceImpl(MemberRepository memberRepository) { this.memberRepository = memberRepository; }
- 즉, MemberServiceImpl 인스턴스가 생성될 때 MemberRepository의 구현체가 주입된다.
- 따라서 Appconfig에 아래와 같이 추가해 주자.
public class AppConfig { public MemberService memberService(){ return new MemberServiceImpl(new MemoryMemberRepository()); } }
- 즉, 다른데서 AppConfig를 통해 MemberService를 불러 쓸 때!!!!! MemoryMemberRepository가 들어간다.
2. OrderServiceImpl 수정
- OrderServiceImpl은 사용하는 필드가 두개이다.
- MemberRepository, DiscountPolicy
- 둘 다 생성자 주입을 받자(참고로 final로 선언하면 기본으로 정해주던가 생성자 주입을 받아야한다.)
public class OrderServiceImpl implements OrderService{ private final MemberRepository memberRepository; //private final DiscountPolicy discountPolicy = new RateDiscountPolicy(); private final DiscountPolicy discountPolicy; //이렇게하면 인터페이스에만 의존함. public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) { this.memberRepository = memberRepository; this.discountPolicy = discountPolicy; }
- 자 이제 OrderServiceImpl은 DIP, OCP를 모두 만족한다.
- DIP : 추상화에만 의존
- OCP : 추상화에만 의존 하기 때문에 구현체를 변경해도 클라이언트에는 코드변경 없음. - AppConfig에 다음과 같이 추가해주자.
public class AppConfig { public MemberService memberService(){ return new MemberServiceImpl(new MemoryMemberRepository()); } public OrderService orderService(){ return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy()); } }
- 앞에서도 말했지만, MemoryMemberRepository()인스턴스를 각각 생성한다.
- 그래서 둘이 다른 객체여서 참조값이 다르지 않을까? 라고 생각했다.
- 하지만 데이터를 저장하기 위해 만든 Map이 static으로 선언되어 있다.
- static은 인스턴스와 무관하게 클래스에 딱! 1개만 생성된다.
- 따라서 모든 인스턴스는 동일한 static 멤버변수를 공유해서 data저장이 다른곳에 될 일은 없다.
3. AppConfig의 책임
- 구현 객체를 생성
- 생성한 객체 인스턴스를 생성자를 통해 주입해줌 --> Injection
4. 변경된 클래스 다이어그램
5. 객체 인스턴스 다이어그램
- appConfig 객체는 memoryMemberRepository를 생성하고,
- 그 참조값을 memberServiceImpl을 생성하면서 생성자로 전달한다.
- 클라이언트인 memberServiceImpl에서 보면 의존 관계를 외부에서 주입해 주는 것 같다.
- 이를 DI(Dependency Injection)이라 한다.
6. 실행
- MemberApp
- AppConfig를 통해 memberService를 생성한다.
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, "A", Grade.VIP);
memberService.join(member);
Member findMember = memberService.findMember(1L);
System.out.println("new Member = " + member.getName());
System.out.println("findMember = " + findMember.getName());
}
}
- OrderApp
public class OrderApp {
public static void main(String[] args) {
AppConfig appConfig = new AppConfig();
MemberService memberService = appConfig.memberService();
OrderService orderService = appConfig.orderService();
//MemberService memberService = new MemberServiceImpl(null);
//OrderService orderService = new OrderServiceImpl(null, null);
Long memberId = 1L;
Member member = new Member(memberId, "memberA", Grade.VIP);
memberService.join(member);
Order order = orderService.createOrder(memberId, "itemA", 10000);
System.out.println("Order : " + order.toString());
System.out.println("Order.calculatedPrice : " + order.calculatedPrice());
}
}
7. Test
- 각 테스트는 독립적이어야 한다.
- 각 테스트 실행 전 appConfig를통해 필요한 인스턴스 생성.
- @BeforeEach활용
MemberService memberService; @BeforeEach public void beforeEach(){ AppConfig appConfig = new AppConfig(); memberService = appConfig.memberService(); } @Test void join(){
8. GitHub : 210725, Separation of concerns
'인프런 > [인프런] Spring 핵심원리 이해' 카테고리의 다른 글
정책을 변경해보자. (0) | 2021.07.25 |
---|---|
Appconfig 리팩터링 (0) | 2021.07.25 |
정책 변경, DIP & OCP 위반 (0) | 2021.07.25 |
새로운 할인 정책 개발 (0) | 2021.07.25 |
주문, 할인 도메인 개발 & Test (0) | 2021.07.25 |
Comments