스프링의 컨테이너와 스프링 빈을 이해하기위해 먼저 DI컨테이너가 무엇이고 왜 생겨났는지 객체지향과 SOLID 원칙의 관점에서 정리를 해보려 한다.
서버와 클라이언트
- A 객체가 B 객체의 메서드를 호출하면, A 객체를 클라이언트 B 객체를 서버라고한다.
- 이러한 개념이 웹 브라우저(client)와 애플리케이션 서버(Server) 개념까지 확장된다.
IoC(Inversion of Control)
- 제어의 역전
- 프로그램의 제어 흐름을 직접 제어하는것이 아니라 외부에서 관리하는것을 IoC라고함
예시를 통해 DI 컨테이너를 왜 사용하는지 알아보자.
그림처럼 할인 정책을 적용할수있는 주문을 구현해보자
public interface DiscountPolicy {
int discount(Member member, int price);
}
public class FixDiscountPolicy implements DiscountPolicy {
private int discountFixAmount = 1000; //1000원 할인
@Override
public int discount(Member member, int price) {
if (member.getGrade() == Grade.VIP) {
return discountFixAmount;
} else {
return 0;
}
}
}
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository = new MemoryMemberRepository();
private final DiscountPolicy discountPolicy = new FixDiscountPolicy
@Override
public Order createOrder(Long memberId, String itemName, int itemPrice) {
Member member = memberRepository.findById(memberId);
int discountPrice = discountPolicy.discount(member, itemPrice);
return new Order(memberId, itemName, itemPrice, discountPrice);
}
}
만약 기존의 정액 할인 정책뿐 아니라 새로운 정률할인 정책을 추가하면 어떻게 될까?
public class RateDiscountPolicy implements DiscountPolicy{
private int discountPercent = 10; //10% 할인
@Override
public int discount(Member member, int price) {
if (member.getGrade() == Grade.VIP){
return price * discountPercent / 100;
}
else {
return 0;
}
}
}
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository = new MemoryMemberRepository();
//private final DiscountPolicy discountPolicy = new FixDiscountPolicy
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
...
}
SOLID 원칙의 관점에서 두가지 문제가 있다.
SOLID 원칙에 대한 내용을 아래의 링크에서 설명했다.
SOLID란?
DiscountPolicy
FixDiscountPolicy
, RateDiscountPolicy
OrderServiceImpl
에 DiscountPolicy
의 구현객체를 대신 생성하고 주입해주어야한다. => DI 컨테이너DI 컨테이너 : 구현 객체를 생성하고 주입해주는 별도의 설정 클래스
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
public OrderService orderService() {
return new OrderServiceImpl(
memberRepository(),
discountPolicy());
}
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
public DiscountPolicy discountPolicy() {
//return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository;
private final DiscountPolicy discountPolicy;
public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
this.memberRepository = memberRepository;
this.discountPolicy = discountPolicy;
}
@Override
public Order createOrder(Long memberId, String itemName, int itemPrice) {
Member member = memberRepository.findById(memberId);
int discountPrice = discountPolicy.discount(member, itemPrice);
return new Order(memberId, itemName, itemPrice, discountPrice);
}
}
public class OrderApp {
public static void main(String[] args) {
AppConfig appConfig = new AppConfig();
MemberService memberService = appConfig.memberService();
OrderService orderService = appConfig.orderService();
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);
}
}