-봄
객체 지향 원칙 적용
AppConfig 리팩토링
– 더 나은 방향으로 문제 및 리팩토링
– AppConfig 코드의 이전 모습 복사역할에 따라 존재 구현이 잘 안됨
리팩토링
- 프레스볼 클래스 X ‘상호 작용‘선택하다!
– AppConfig 리팩토링
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
private MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
public OrderService orderService() {
return new OrderServiceImpl(
memberRepository(), discountPolicy());
}
public DiscountPolicy discountPolicy() {
return new FixDiscountPolicy();
}
}
- MemberService 역할, MemberRepository 역할 및 OrderService 역할이 모두 표시됩니다.
- DiscountPolicy 역할이 추가되었습니다!
- 장점: 각 역할을 알 수 있고(메소드 이름을 보면) 쉽게 변경할 수 있습니다.
- new MemoryMemberRepository() → 이 부분의 중복 제거
- MemoryMemberRepository → 다른 구현으로 변경 시 한 부분만 변경하면 됩니다.
- AppConfig를 보면 역할과 구현 클래스가 한눈에 보이므로 전체 애플리케이션이 어떻게 구성되어 있는지 빠르게 확인할 수 있습니다.
새로운 구조 및 할인 정책 적용
– 상단으로 돌아가서 균일할인 정책을 균일(%)할인 정책으로 변경
– FixDiscountPolicy RateDiscountPolicy
– AppConfig만 변경하면 됩니다.
: 응용 프로그램이 큽니다.
응용 분야수업, 객체가 생성되고 구성되는 영역로 나누어
용도와 구성의 분리
- 할인 정책이 변경되면 AppConfig 부분만 변경하면 됩니다.
- 또한 FixDiscountPolicy → RateDiscountPolicy로 이동하면 구성 영역만 영향을 받고응용 분야는 전혀 영향을 받지 않습니다.
public DiscountPolicy discountPolicy() {
return new RateDiscountPolicy();
}
- RateDiscountPolicy(); 그냥 수리!
수정 코드 테스트
- 당신이 성공했는지 확인
주문 앱
- OrderApp에서 main()을 테스트해보면 값이 잘 나오는 것을 확인할 수 있습니다.
할인 정책을 변경하더라도 이제 애플리케이션을 구성하는 역할을 하는 AppConfig만 변경하면 됩니다.
OrderServiceImpl 고객 코드를 포함하여 사용 영역에서 코드를 변경할 필요가 없습니다.
구성 영역은 물론 변경됩니다.
– 구성 역할을 하는 AppConfig를 Application이라는 공연의 주최자로 생각할 수 있습니다.
공연의 주최자는 공연에 관련된 모든 구현 객체를 알아야 합니다.
DIP 및 OCP 모두 준수
전체 흐름 정리
새로운 할인 정책 개발
– 다형성 덕분에 신규 정가 할인코드 추가 개발이 용이합니다.할인 정책의 신규 적용과 문제점은?
– 새로 개발된 정액할인 정책을 적용하기 위해서는 주문서비스 구현인 고객코드도 변경되어야 합니다.
– 주문 서비스 클라이언트는 DisCountPolicy 인터페이스뿐만 아니라 구체적인 클래스인 FixDisCountPolicy에도 의존 → DIP 위반
주문 서비스 고객이 너무 많이 했습니다.
관심사 분리
– 과거 → 클라이언트가 의존하는 서버 구현을 직접 빌드하고 실행
– 프로모터: AppConfig 출시
– AppConfig는 애플리케이션의 전체 동작 방식을 설정(configuration)하기 위한 것으로, 구현 개체 만들기하다 연결에 대한 책임
– 이제부터 클라이언트 개체는 권한이 축소된 상태에서 역할 수행에만 집중합니다(책임이 명확해짐).AppConfig 리팩토링
– 명확한 역할 분리 및 구성 정보 구현
– 역할이 잘 정의되어 있습니다.
– 중복 제거새로운 구조 및 할인 정책 적용
– 균일할인제 → 균일할인제(%)로 변경됩니다.
– AppConfig의 출현으로 애플리케이션이 강력해지고 있습니다.
응용 분야그리고 객체 생성 및 구성 영역통해 분리
– 할인 정책을 변경하더라도 → AppConfig가 위치한 구성 영역을 살펴보기만 하면 되고, 사용 영역을 변경해야 함 x 고객 코드인 주문 서비스 코드도 변경되지 않음은 물론
좋은 객체지향 설계의 5가지 원칙 적용
예제에서 SRP, DIP, OCP 사용
– SRP 단일 책임 원칙(1클래스 1책임)
- 클라이언트 개체(예: OrderService 등)는 직접 구현 개체를 생성, 연결 및 실행하는 데 서로 다른 책임이 있습니다.
- 개인 책임의 SRP 원칙에 따라 기업 영역 분리
- AppConfig는 구현 개체를 만들고 연결하는 역할을 합니다.
- 클라이언트 개체는 실행만 담당합니다.
– DIP 종속성 역전 원칙(추상화에 의존, 구체화에 의존 X – 종속성 주입은 이 원칙을 따르는 한 가지 방법)
- 새로운 할인 정책을 개발하여 적용하려면 클라이언트 코드도 변경 → 기존 클라이언트 코드(OrderServiceImpl)는 DIP를 따르고 DiscountPolicy 추상화 인터페이스에 의존하는 것처럼 보였지만 수정할인 정책 또한 구체적인 구현 클래스에 따라 다릅니다.
- 고객 코드입니다 할인 정책 추상화 인터페이스에만 의존하도록 코드 수정 → 클라이언트 코드는 인터페이스만으로는 아무것도 할 수 없음
- AppConfig 수정할인 정책 클라이언트 코드 대신 개체 인스턴스를 생성하여 클라이언트 코드에 종속성 주입 → DIP 원칙 준수, 문제 해결
– OCP(확장에는 소프트웨어 항목이 열려 있어야 하지만 변경에는 닫혀 있어야 함)
- 다형성 사용, 클라이언트는 DIP 관찰
- 애플리케이션을 사용 및 구성 영역으로 나누기
- AppConfig 수정 종속성할인 정책 비율할인 정책 클라이언트 코드에 주입되므로 클라이언트 코드를 변경할 필요가 없습니다.
- 소프트웨어 요소를 새롭게 확장해도 응용분야 변경은 완료!
→ 변경이 종료됨: 변경할 필요가 없음
IoC, DI, 컨테이너
– Inversion of Control IoC(Inversion of Control)
– 기존 프로그램 → 클라이언트 구현체는 자신이 필요로 하는 서버 구현체를 생성하고 연결하여 실행한다.
– 구현 객체는 프로그램 자체의 제어 흐름을 제어 – 개발자에게는 자연스러운 흐름AppConfig 소개
– 구현 객체는 자신의 로직 실행만 담당 → 프로그램의 제어 흐름 AppConfig에서 가져옴
(OrderServiceImpl은 필요한 인터페이스를 호출하지만 어떤 구현 개체가 실행되는지 알지 못합니다.
프로그램에 대한 제어 흐름에 대한 주권: AppConfig에는)
– OrderServiceImpl도 AppConfig에 의해 생성됩니다.
– AppConfig는 OrderServiceImpl이 아닌 OrderService 인터페이스의 다른 구현 개체도 생성하고 실행할 수 있습니다.
주문 서비스 임플 몰라, 그냥 네 논리를 실행
– IoC(Inversion of Control)는 프로그램의 제어 흐름이 직접이 아닌 외부에서 관리될 때 발생합니다.내가 제어하고 호출하지 않지만 프레임워크가 대신 호출합니다.
컨트롤 역전 → 컨트롤 역전
AppConfig는 Impl(구현)을 선택합니다.
프레임워크와 라이브러리
내가 작성한 코드 제어, 대신 실행 → Framework ok (JUnit) 액자
내가 작성한 코드는 제어 흐름을 직접 담당합니다 도서관
– 중독 주사
– OrderServiceImpl은 DiscountPolicy 인터페이스에 따라 다름 → 어떤 구현이 실제로 사용되는지 알 수 없음
– 종속성: 정적 클래스 종속성 / 런타임에 결정됨 동적 개체(인스턴스) 종속성 관계 둘을 따로 생각하다– 정적 클래스 종속성 : 클래스가 사용하는 import 코드만 봐도 쉽게 의존관계를 파악할 수 있고, 어플리케이션을 실행하지 않고도 분석 가능 → 클래스 코드만 열어서 사용 가능
– OrderServiceImpl이 MemberRepository 및 DiscountPolicy에 종속되어 있음을 알 수 있습니다.
이러한 종속성만 실제로 OrderServiceImpl에 어떤 개체가 주입되는지 알지 못합니다.
클래스 다이어그램
종속성 다이어그램
종속성 표시
의존
- 종속성 확인 가능
동적 개체 인스턴스의 종속성
– 응용 프로그램이 실행될 때 실제로 생성된 개체 인스턴스에 대한 참조에 연결된 종속성
– DI(Dependency Injection): 애플리케이션 실행 시간(런타임)에 외부에서 실제 구현 객체를 생성하고 이를 클라이언트에 전달하여 클라이언트와 서버 간의 실제 종속 관계를 설정합니다.
– 객체 인스턴스를 생성하고 참조 값을 전달하여 연결
– 의존성 주입 사용 → 클라이언트 코드 변경 없이 클라이언트 호출 대상의 타입 인스턴스 변경 가능/ 정적 클래스 종속성을 변경하지 않고 동적 객체 인스턴스의 종속성을 쉽게 변경(애플리케이션 코드 변경 없음)
IoC 컨테이너, DI 컨테이너
– (AppConfig와 유사) 객체 생성 및 관리 시 링크 종속성기능: IoC 컨테이너 또는 DI 컨테이너
– 최근 DI 컨테이너/또는 어셈블러, 객체 팩토리 등으로 불리는 종속성 주입에 중점을 둡니다.
봄으로 전환
– 기존에는 순수 Java 코드로만 DI를 적용했지만 이제는 Spring을 사용하세요!
– AppConfig Spring 기반으로 수정
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(
memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy() {
return new RateDiscountPolicy();
}
}
- @Configuration 주석 추가 – 설정 및 구성 정보 담당
- 사용 중인 가져오기가 올바른지 항상 확인해야 합니다.
- 모든 메소드는 Spring 컨테이너에 등록됩니다.
– MemberApp에 펜 컨테이너 적용
public class MemberApp {
public static void main(String() args) {
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
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());
}
}
- 봄에는 모든 것이 ApplicationContext로 시작됩니다.
- ApplicationContext: Spring Container – 모든 것을 관리(Object – @Bean)
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
- 주석 기반 구성
- AppConfig를 파라미터로 입력 AppConfig의 환경 설정 정보로 Spring은 @Bean에 속하는 메소드를 Spring 컨테이너에 등록하고 관리합니다.
applicationContext.getBean("memberService", MemberService.class);
- (“이름”, 유형)
- AppConfig에서 사용할 항목
- 두 번째 녀석!
- 기본적으로 메서드 이름으로 등록
시작 메인()
- 위의 5개는 Spring에 의해 내부적으로 등록됩니다.
- 아래 5개는 @Bean으로 등록되어 Spring 컨테이너에 등록된다.
- 이름 = 키, 값 = 반환 값
- 전. memberService = 키 / 새 MemberServiceImpl() – 개체 인스턴스 = 값
– 펜 컨테이너를 OrderApp에 적용
public class OrderApp {
public static void main(String() args) {
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
OrderService orderService = applicationContext.getBean("orderService", OrderService.class);
Long memberId = 1L;
Member member = new Member(memberId, "memberA", Grade.VIP);
memberService.join(member);
Order order = orderService.createOrder(
memberId, "itemA", 20000);
System.out.println("order = " + order);
// System.out.println("order.calculatePrice = " + order.calculatePrice());
}
}
- MemberApp과 같은 코드 수정
- 잘 작동하는 것을 확인할 수 있습니다
– 펜 용기?
– ApplicationContext = 스프링 컨테이너
– 기존에는 개발자가 AppConfig, DI로 직접 객체를 생성했으나 이를 Spring 컨테이너를 통해 사용
– 설정(구성) 정보로 @Configuration과 함께 AppConfig 사용
모든 @Bean 메소드를 호출하여 반환된 객체를 Spring 컨테이너에 등록 → Spring 컨테이너에 등록된 객체 이렇게 = 봄 콩
– 접두사 @Bean 메서드 이름두번째 스프링 빈 이름(memberService, orderService)로 사용
– 기존에는 개발자가 AppConfig로 직접 필요한 객체를 검색 → 스프링 컨테이너를 통해 필요한 스프링 빈(오브젝트)을 찾아야 합니다.
– 봄콩 applicationContext.getBean() 방법을 사용하여 찾을 수 있습니다
– Java 코드로 다 제가 직접 했어요 Spring Container에 Object를 Bean으로 등록 → Spring Container에서 Spring Bean을 찾아서 사용하도록 수정!
(@Autowired와 동일)
→ 코드가 더 복잡해진 것 같은데 장점은?
– (Spring Core Principles – Basic) 강의를 듣고 공부하려고 썼습니다.