여기서 3가지. SRP, DIP, OCP 적용
한 클래스는 하나의 책임만 가져야 한다.
프로그래머는 추상화에 의존해야지, 구체화에 의존하면 안된다. 의존성 주입은 이 원칙을 따르는 방법 중 하나.
OrderServiceImp
은 DIP를 지키며 DiscountPolicy
추상화 인터페이스에 의존하는 것 같았지만, FixDiscountPolicy
구체화 구현 클래스에도 함께 의존했다.DiscountPolicy
추상화 인터페이스에만 의존하도록 코드를 변경했다.FixDiscountPolicy
객체 인스턴스를 클라이언트 코드 대신 생성해서 클라이언트 코드에 의존관게를 주입했다.소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다.
FixDiscountPolicy
에서 RateDiscountPolicy
로 변경해서 클라이언트 코드에 주입하므로 클라이언트 코드는 변경하지 않아도 됨.OrderServiceImpl
은 필요한 인터페이스들을 호출하지만 어떤 구현 객체들이 실행될지 모른다.OrderServiceImpl
도 AppConfig가 생성한다.OrderServiceImp
이 아닌 OrderServie
인터페이스의 다른 구현 객체를 생성하고 실행할 수도 있다.OrderServiceImpl
은 묵묵히 자신의 로직을 실행할 뿐이다.OrderServiceImpl
은 DiscountPolicy
인터페이스에 의존한다.OrderServiceImpl
은 MemberRepository
, DiscountPolicy
에 의존한다는 것을 알 수 있다.OrderServiceImpl
에 주입될지 알 수 없다.애플리케이션 실행 시점에 실제 생성된 객체 인스턴스의 참조가 연결된 의존 관계다.
지금까지 순수한 자바 코드만으로 DI를 적용했다. 이제 스프링을 사용해보자!
@Configuration
public class AppConfig {
@Bean
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
@Bean
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy() {
return new RateDiscountPolicy();
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
public class MemberApp {
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService = applicationContext.getBean("memberService", MemberService.class);
...
}
}
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", 10000);
System.out.println("order = " + order);
}
}
ApplicationContext
를 스프링 컨테이너라 한다.AppConfig
를 사용해 직접 객체를 생성하고 DI를 했지만, 이제부터는 스프링 컨테이너를 통해 사용한다.@Configuration
이 붙은 AppConfig
를 설정(구성)정보로 사용한다. 여기서 @Bean
이라 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록한다.@Bean
이 붙은 메서드의 명을 스프링 빈의 이름으로 사용한다. (memberService
, orderService
)@Bean(name = "{이름}")
AppConfig
를 통해서 직접 조회했지만, 이제부터는 스프링 컨테치언를 통해서 필요한 스프링 빈(객체)를 찾아야 한다.applicationContext.getBean()
메서드를 사용해서 찾을 수 있다.대충 어마어마한 장점이 있다고 하는데…