식당에서 주문을 QR코드를 통해 받는 예시를 생각해보자
(코드는 SpringBoot를 사용하지 않고 Java로만 작성)
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
private final OrderPolicy orderPolicy = new QROrderPolicy();
//...
위의 코드를 보면
OrderServiceImpl은 추상체 MemberRepository와 구현체 MemoryMemberRepository에
그리고 추상체 OrderPolicy와 구현체 QROderPolicy에 모두에 의존하고 있다.
➡️ DIP 위반
또한 식당에서 QR코드를 통해 주문을 받는 것에서 태블릿을 통해 주문을 받는 것으로 주문 방식을 변경하고자 하는 상황을 생각해보자.
주문 정책을 변경하고자 한다면 클라이언트인 OrderServiceImpl을 수정해야 한다.
public class OrderServiceImpl implements OrderService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
private final OrderPolicy orderPolicy = new TabletOrderPolicy();
//...
기능을 확장해서 변경하면 클라이언트 코드에 영향을 준다.
➡️ OCP 위반
역할과 구현을 분리하였고, 다형성을 활용해 인터페이스와 구현 객체를 분리하였으나
객체지향 설계 원칙(OCP, DIP)을 준수하지 못하였다.
(의존 관계를 변경하더라도 구현체가 없으면 코드를 실행할 수 없음)
애플리케이션의 젼체 동작 방식을 구성(config)하기 위한 AppConfig 사용
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(memberRepository());
}
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
public OrderService orderService() {
return new OrderServiceImpl(memberRepository(), orderPolicy());
}
public OrderPolicy orderPolicy() {
// 구현체를 변경할 때에는 이 부분만 수정하면 됨
return new TabletOrderPolicy();
}
}
AppConfig를 통해서 관심사를 확실하게 분리함
public class OrderServiceImpl implements OrderService{
private final MemberRepository memberRepository;
private final OrderPolicy orderPolicy;
public OrderServiceImpl(MemberRepository memberRepository, OrderPolicy orderPolicy) {
this.memberRepository = memberRepository;
this.orderPolicy = orderPolicy;
}
}
//...
public class OrderApp {
public static void main(String[] args) {
AppConfig appConfig = new AppConfig();
MemberService memberService = appConfig.memberService();
OrderService orderService = appConfig.orderService();
//...
appConfig 객체는 memoryMemberRepository 객체를 생성하고 그 참조값을 orderServiceImpl을 생성하면서 생성자로 전달
클라이언트인 orderServiceImpl 입장에서 보면 의존관계를 외부에서 주입해주는 것 같다고 하여
DI(Dependency Injection), 의존 관계 주입이라 한다.
OrderServiceImpl은 OrderPolicy에만 의존, 구현체에는 의존하지 않음
OrderServiceImpl은 MemberRepository에만 의존, 구현체에는 의존하지 않음
OrderServiceImpl은 생성자를 통해 어떤 구현 객체가 주입될지는 알 수 없음
OrderServiceImpl의 생성자를 통해서 어떤 구현 객체를 주입할지는 오직 AppConfig가 결정
➡️ OrderServiceImpl은 실행에만 집중
🔹 AppConfig는 애플리케이션의 실제 동작에 필요한 구현 객체를 생성
🔹 AppConfig는 생성한 객체 인스턴스의 참조(레퍼런스)를 생성자를 통해서 주입(연결)
인프런 스프링 핵심 원리 - 기본편 (김영한) 참조
좋은 글 감사합니다.