-
Notifications
You must be signed in to change notification settings - Fork 1
기본 : Section 3
DIP : 클래스 의존 관계 확인
실제 의존 관계
- 역할(
DiscountPolicy
)뿐만 아니라 구현(RateDiscountPolicy
)에도 의존하고 있음 → DIP 위반
<해결>
: 인터페이스에만 의존하도록 코드를 변경해야 함!!
// private final DiscountPolicy discountPolicy = new RateDiscountPolicy(); // 고정 할인 정책
private DiscountPolicy discountPolicy; // 인터페이스인 DiscountPolicy에만 의존 -> DIP 지킬 수 있음
→ 하지만 실제로 실행하면 Null Pointer Exception 에러 발생 ⇒ 파라미터로 구현 객체를 누군가가 대신 생성해서 주입해줘야 함
OCP : 변경하지 않고 확장 가능한가?
-
할인 정책을
FixDiscountPolicy
에서RateDiscountPolicy
로변경하려면OrderServiceImpl
코드를 고쳐야 함// private final DiscountPolicy discountPolicy = new FixDiscountPolicy(); // 고정 할인 정책 private final DiscountPolicy discountPolicy = new RateDiscountPolicy(); // 정률 할인 정책
-
현재 코드 : 확장 시, 클라이언트 코드 수정해야 하므로 OCP 위반
OrderServiceImpl
이 직접 객체를 생성하고 할인 정책을 선택해서 할당함
AppConfig
AppConfig ? 애플리케이션의 실제 동작에 필요한 구현 객체를 생성하고, 생성한 객체 인스턴스의 레퍼런스를 생성자를 통해서 주입(연결) 하는 별도의 설정 클래스
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(new MemoryMemberRepository());
}
}
- AppConfig를 통해 memberService를 부름 (생성자 주입)
→
MemberRepositoryImpl
의 생성자를 통해 객체 생성,MemberRepositoryImpl
에는 구현 의존 X , 오로지 인터페이스에만 의존 -
MemberServiceImpl
입장에서 생성자를 통해 어떤 구현 객체가 들어올지는 알 수 없음 -
MemberServiceImpl
의 생성자를 통해서 어떤 구현 객체를 주입할지는 오직AppConfig
에서 결정됨
<클래스 다이어그램>
AppConfig appConfig = new AppConfig();
MemberService memberService = appConfig.memberService();
- AppConfig를 사용해서 memberService를 호출 및 사용하게 해줌
AppConfig : 애플리케이션이 크게 사용 영역과, 객체를 생성하고 구성(Configuration)하는 영역으로 분리가 가능해짐
- 할인 정책을 변경해도 구성 영역인 AppConfig만 수정해도 됨, 사용 영역에는 영향 X
여기서는 SRP, DIP, OCP 적용
SRP
- 클라이언트 객체는 직접 구현 객체를 생성하고, 연결하고, 실행하는 다양한 책임을 가지고 있음
- SRP 단일 책임 원칙을 따르면서 관심사를 분리함
- 구현 객체를 생성하고 연결하는 책임은 AppConfig가 담당
- 클라이언트 객체는 실행하는 책임만 담당
DIP
- 클라이언트 코드가
DiscountPolicy
추상화 인터페이스에만 의존하도록 코드 변경 -
AppConfig
가FixDiscountPolicy
객체 인스턴스를 클라이언트 코드 대신 생성해서 클라이언트 코드에 의존관계 주입
OCP
- 애플리케이션을 사용 영역과 구성 영역으로 나눔
-
AppConfig
가 의존관계를FixDiscountPolicy
RateDiscountPolicy
로변경해서 클라이언트 코드에 주입 -> 클라이언트 코드 :변경 필요 X
제어의 역전 IoC (Inversion of Control)
- 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리
- AppConfig : 프로그램의 제어 흐름에 대한 권한 가짐 → 구현 객체 : 자신의 로직을 실행하는 역할만 담당함 (흐름 제어권 X)
<프레임워크 vs 라이브러리>
- 프레임워크 : 내 코드를 제어하고 대신 실행 (JUnit, test framework)
- 라이브러리 : 코드가 직접 제어의 흐름 담당 (내가 라이브러리를 직접 호출함)
의존관계 주입 DI(Dependency Injection)
의존관계 주입 ? 애플리케이션 실행 시점에서 외부에서 실제 구현 객체를 생성하고 클라이언트에 전달해서 클라이언트와 서버의 실제 의존관계가 연결되는 것
- 정적인 클래스 의존 관계 와 실행 시점에 결정되는 동적인 객체 의존 관계 들을 분리해서 생각해야 함
- 코드에서는 AppConfig가 의존관계를 주입해줌
-
정적인 클래스 의존 관계
- import 보고 의존 관계 파악 가능
-
동적인 객체 인스턴스 의존 관계
- 애플리케이션 실행 시점에 실제 생성된 객체 인스턴스의 참조 보고 의존 관계 파악 가능
- 실행을 해봐야 알 수 있음
<장점>
- 클라이언트 코드 변경 없이, 클라이언트가 호출하는 대상의 타입 인스턴스 변경 가능
- 정적인 클래스 의존 관계 변경 없이, 동적인 객체 인스턴스 의존 관계 쉽게 변경 가능
컨테이너
컨테이너 : AppConfig 처럼 객체를 생성하고 관리하면서 의존관계를 연결해주는 것
- IoC 컨테이너, DI 컨테이너, 어셈블러, 오브젝트 팩토리 등으로 불림
- 현재는 DI 컨테이너라는 용어를 사용함
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
.
.
.
-
@Configuration
: AppConfig에 설정 구성 -
@Bean
: 스프링 컨테이너에 스프링 빈으로 등록
→ 스프링 컨테이너 : @Configuration이 붙은 AppConfig를 설정 정보로 사용함
// 스프링 컨테이너 : ApplicationContext
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
getBean
: memberService 라는 이름의 메서드를 가진 스프링 빈을 찾음