관심사 분리
MemberApp 변경전
public class MemberApp {
public static void main(String[] args) {
MemberService memberService = new MemberServiceImpl();
Member member = new Member(1L, "memberA", Grade.VIP);
memberService.join(member);
Member findMember = memberService.findMember(1L);
System.out.println("findMember = " + member.getName());
System.out.println("findMember = " + findMember.getName());
}
}
변경코드
public static void main(String[] args) {
AppConfig appConfig = new AppConfig(); //이거 무조건 만들어야댐
MemberService memberService = appConfig.memberService();
이하 동일
변경 전 코드는 멤버서비스 인플에서 메모리 멤버 리파지토리를 만드는데
변경 후 코드는 appConfig에서 다 결정한다. appConfig가 멤버서비스 인플을 반환을 하니까 memberService에는 멤버서비스 인플이 들어가있다.
OrderApp
public static void main(String[] args) {
AppConfig appConfig = new AppConfig();
MemberService memberService = appConfig.memberService();
OrderService orderService = appConfig.orderService();
AppConfig
public OrderService orderService() {
return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy()); //AppConfig에서 구현객체를 생성함
}
OrderApp의 orderService를 보자
AppConfig에서 OrderServiceImpl 객체가 생성되고 MemoryMemberRepository와 FixDiscountPolicy가 생성자로 넘어간다 OrderServiceImpl이 MemoryMemberRepository와 FixDiscountPolicy객체를 참조하도록 하고 완성된 OrderServiceImpl 객체를 반환한다.
MemberServiceTest 변경 전
public class MemberServiceTest {
MemberService memberService = new MemberServiceImpl();
변경 후
public class MemberServiceTest {
MemberService memberService;
@BeforeEach
public void beforeEach() { //테스트 실행하기 전에 수행됨
AppConfig appConfig = new AppConfig();
memberService = appConfig.memberService();
}
팁 컨트롤 + E + 엔터 : 이전에 했던 파일로 돌아감
AppConfig 리팩터링
역할에 따른 어떤 구현을 하는지가 한눈에 보여야한다

팁:컨트롤 + 알트 + M : 리팩토링할때 중복제거
public class AppConfig { //나의 애플리케이션 전체에 대해 설정하고 구성하는 공간
public MemberService memberService() { //AppCofig를 쓸때 멤버서비스 씀
return new MemberServiceImpl(MemberRepository()); //멤버서비스 구현체인 객체가 생성됨
}
private MemoryMemberRepository MemberRepository() {
return new MemoryMemberRepository();
}
public OrderService orderService() {
return new OrderServiceImpl(MemberRepository(), discountPolicy()); //AppConfig에서 구현객체를 생성함
}
public DiscountPolicy discountPolicy() {
return new FixDiscountPolicy();
}
}
리팩토링을 통해 MemberService 역할, MemoryMemberRepository 역할, OrderService 역할, discountPolicy 역할이 드러나게 된다. OrderService 에서 discountPolicy()를 둠으로써 FixDiscountPolicy()를 콜하도록 한다.
이점:메소드명만 봐도 역할이 잘 드러나게 된다. 애플리케이션 전체 구성이 어떻게 되어있는지 빠르게 파악할 수 있다
새로운 구조와 할인 정책 적용
AppCofing:구체적인거에 대해서 잘 아는 책임

너무 중요한 그림!
사용영역에 있는 코드는 전혀 손대지 않고 구성 영역의 코드만 바꿀 수 있다
IoC, DI, 그리고 컨테이너

정적인 클래스 의존관계는 클래스 코드만 봐도 알 수 있는데 실제 어떤 객체가 OrderServiceImpl에 주입될지는 알 수 없다
메모리 리포지토리에 메모리멤버리포지토리가 들어올지 db메모리리포지토리가 들어올지, 할인정책에 정률 정책이 들어올지 정액 정책이 들어올지 알 수 없다
동적인 객체 인스턴스 의존관계

객체다이어그램은 애플리케이션이 실행할때마다 동시에 바뀐다
정적인 그림은 하나도 손댈 필요없이 동적인 그림만 바꾸면 된다. 정적인 그림에 손을 대지 않는다는 것은 의존관계에 손대지 않는다는 말 =>DI의 장점
스프링으로 전환하기
@Configuration
public class AppConfig { //나의 애플리케이션 전체에 대해 설정하고 구성하는 공간
//멤버서비스에 대한 구현을 나의 애플리케이션에서는 멤버서비스 인플을 쓸거야
@Bean
public MemberService memberService() { //AppCofig를 쓸때 멤버서비스 씀
return new MemberServiceImpl(MemberRepository()); //멤버서비스 구현체인 객체가 생성됨
}
//나의 애플리케이션에서는 멤버 리포지토리는 메모리멤버리포지토리로 할거야 -> 나중에 jdbc 리포지토리로 바껴도 이 코드만 바꾸면 된다.
@Bean
public MemoryMemberRepository MemberRepository() {
return new MemoryMemberRepository();
}
//오더서비스에 대한 구현을 나의 애플리케이션에서는 오더서비스 인플을 쓸건데 멤버리포지토리는 메모리멤버리포지토리, 디스카운트폴리시는 픽스디스카운트 폴리시 쓸거야
@Bean
public OrderService orderService() {
return new OrderServiceImpl(MemberRepository(), discountPolicy()); //AppConfig에서 구현객체를 생성함
}
@Bean
public DiscountPolicy discountPolicy() {
//return new FixDiscountPolicy();
//할인클래스를 구체 객체로 바꾸기
return new RateDiscountPolicy();
}
}
@Configuration 하면 애플리케이션의 설정정보를 나타냄
각메소드에 @Bean을 달면 메소드명으로 스프링 컨테이너에 등록하게 됨
ApplicationContext : 스프링컨테이너 @Bean 달은 애들을 다 관리
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig.class 에 있는 환경 설정 정보를 가지고 스프링이 @Bean 달린 애들을 넣어 관리해줌
변경 전(자바)
public class MemberApp {
public static void main(String[] args) {
// AppConfig appConfig = new AppConfig(); //이거 무조건 만들어야댐
// MemberService memberService = appConfig.memberService();
변경 후(스프링)
public class MemberApp {
public static void main(String[] args) {
// AppConfig appConfig = new AppConfig(); //이거 무조건 만들어야댐
// MemberService memberService = appConfig.memberService();
//MemberService memberService = new MemberServiceImpl();
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
변경전에는 직접 찾아왔다면 변경후에는 스프링컨테이너를 통해 찾아온다
AppConfig에서 @Bean이 달린 메소드가 스프링컨테이너에 등록될때 메소드명으로 등록되는데 이걸 getBean 첫번째 파라미터에 적으면 '이 이름으로 이 객체를 찾을거야'라는 뜻이 된다. 두번째는 반환타입이다.
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();
//OrderService orderService = new OrderServiceIml();
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
orderService 꺼내보면
AppConfig의 orderService에서 리턴한 OrderServiceIml이 나온다
@Bean
public OrderService orderService() {
return new OrderServiceImpl(MemberRepository(), discountPolicy()); //AppConfig에서 구현객체를 생성함
}
'Springboot' 카테고리의 다른 글
| 스프링 핵심 원리-기본편 섹션 2. 스프링 핵심 원리 이해1 정리 (0) | 2024.03.15 |
|---|---|
| 스프링 핵심 원리-기본편 섹션 1. 객체 지향 설계와 스프링 정리 (0) | 2024.03.15 |
| 김영한 스프링 입문 섹션 7. AOP 정리 (0) | 2024.03.10 |
| 김영한 스프링 입문 섹션 6.스프링 DB 접근 기술 정리 (0) | 2024.03.09 |
| 김영한 스프링 입문 섹션 5.회원 관리 예제-웹 MVC 개발 정리 (0) | 2024.03.08 |