스프링을 사용하는 이유 (2) - OCP와 DIP를 지키자!

Chan Young Jeong·2023년 2월 8일
0

스프링

목록 보기
2/7

지난 시간

지난 시간에 작성한 코드를 봐보겠습니다.

OrderServiceImpl

public class OrderServiceImpl implements OrderService{

	private final MemberRepository memberRepository = new MemoryMemberRepository();
 	private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
    
    void createOrder(String memberName, String itemName, int itemPrice){
    	
        memberRepository.findByName(memberName);
        discountPolicy.discount(itemPrice);
        System.out.println( itemName + "주문이 완료 되었습니다.")
    
    }
}

클라이언트 코드인 OrderServiceImpl은 인터페이스 뿐만 아니라 구체 클래스도 의존합니다. 그리고 구체 클래스를 변경하려면(정액제->정률제) 클라이언트 코드 또한 변경해야합니다. (OCP, DIP 둘다 위반!)

지난 시간 김영한님의 말을 다시 인용하면,

"로미오와 줄리엣 공연을 하면 로미오 역할을 누가 할지 줄리엣 역할을 누가 할지는 배우들이 정하는게 아니다. 이전 코드는 마치 로미오 역할(인터페이스)을 하는 레오나르도 디카프리오(구현체, 배우)가 줄리엣 역할(인터페이스)을 하는 여자 주인공(구현체, 배우)을 직접 초빙하는 것과 같다. 디카프리오는 공연도 해야하고 동시에 여자 주인공도 공연에 직접 초빙해야 하는 다양한 책임을 가지고 있다."

즉 배우는 배우에만 몰두하고, 공연 장소를 정하고 어떤 배우를 케스팅할지는 공연기획자가 결정해야합니다. 즉 공연 기획자가 필요한데, 자바에서는 AppConfig 클래스에서 담당합니다.


바꿔봅시다!

일단 클라이언트 코드가 추상 클래스에만 의존하도록 코드를 변경하겠습니다.

public class OrderServiceImpl implements OrderService{

	private final MemberRepository memberRepository; // 더이상 구체 클래스 직접 지정 X
 	private final DiscountPolicy discountPolicy;
    
    void createOrder(String memberName, String itemName, int itemPrice){
    	
        memberRepository.findByName(memberName);
        discountPolicy.discount(itemPrice);
        System.out.println( itemName + "주문이 완료 되었습니다.")
    
    }
}

  • 인터페이스에만 의존하도록 코드 변경 완료
  • 그런데 구현체가 없는데 코드를 실행할 수 있을까?
  • 당연히 Nullpointer Exception이 발생

해결방안
누군가 OrderServiceImpl에 DiscountPolicy, MemberRepository의 구현 객체를 대신 생성해주고 주입해주어야 한다.

그 역할을 이제 AppConfig가 대신 해줄 겁니다! 변경 사항이 있으면 이제 AppConfig에서만 코드를 바꿔주면 됩니다! DbMemberRepository로 바꾸고 싶다면 new MemoryMemberRepository()를 new DbMemberRepository로 바꿔주기만 하면 됩니다!

public class AppConfig{

 	public OrderService orderService() {
	 return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
 }

}

public class OrderServiceImpl implements OrderService{

	private final MemberRepository memberRepository; // 더이상 구체 클래스 직접 지정 X
 	private final DiscountPolicy discountPolicy;
    
    
    public OrderServiceImpl(MemberRepository memberRepository,DiscountPolicy discountPolicy){
    	this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
    
    
    void createOrder(String memberName, String itemName, int itemPrice){
    	
        memberRepository.findByName(memberName);
        discountPolicy.discount(itemPrice);
        System.out.println( itemName + "주문이 완료 되었습니다.")
    
    }
}

이제는 구현체가 바뀌어도 클라이언트 코드를 바꿀 필요 없이 AppConfig 설정 파일에서 구현체만 갈아끼면 됩니다! (OCP 해결) 그리고 OrderServiceImpl에서는 구현체를 더이상 의존하지 않고 인터페이스에만 의존할 수 있게 되었습니다.(DIP 해결)

이렇게 해서 SOLID를 만족시키면서 코드를 작성해보았습니다. 하지만 지금은 주입할 객체가 적어서 AppConfig를 만들어 여기다가 선언하는 것이 그렇게 어렵지 않았습니다. 하지만 코드가 많아지 경우 AppConfig만으로는 당연히 힘들어 지겠죠?

그래서 이를 해결해주는 것이 바로 스프링의 DI 컨테이너입니다! 다음에는 스프링을 이용해서 다시 한번 코드를 변경해보겠습니다!

출처
김영한의 스프링 핵심원리

0개의 댓글