[Spring] Spring Basic 02
DIP, OCP 예제
public class OrderServiceImpl implements OrderService {
// private final DiscountPolicy discountPolicy = new FixDiscountPolicy();
private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
}
위의 코드만 보았을때, 역할과 구현을 분리하고, 다형성 활용, 인터페이스와 구현객체를 분리하는 등 OCP, DIP 같은 객체지향 설계원칙을 준수한 것으로 보인다.
하지만 클래스 의존관계를 보면
기대했던 의존관계와 달리,
인터페이스(DiscountPolicy) 뿐 아니라 구현 클래스(FixDiscountPolicy, RateDiscountPolicy)에도 의존하고 있으므로, DIP 위반이다.
현재 코드대로라면 기능을 확장해서 변경할때, 클라이언트 코드에 영향을 주므로 OCP를 위반한다고 할 수 있다.
문제 해결 방법
간단하게 인터페이스에만 의존하도록 코드를 변경하면 된다.
public class OrderServiceImpl implements OrderService {
//private final DiscountPolicy discountPolicy = new RateDiscountPolicy();
private DiscountPolicy discountPolicy;
}
하지만 이대로면 구현객체가 없어 NullPointerException이 뜬다.
따라서 외부에서 주입할 구현객체를 결정하도록 만들어야한다.
- AppConfig
AppConfig는 앞서 설명한 애플리케이션의 전체 동작 방식을 구성(Config)하기 위해, 구현객체를 생성하고, 연결하는 책임을 가지는 별도의 설정클래스이다.public class AppConfig { public MemberService memberService() { return new MemberServiceImpl(new MemoryMemberRepository()); } public OrderService orderService() { return new OrderServiceImpl( new MemoryMemberRepository(), new FixDiscountPolicy()); }
이 AppConfig에서 생성한 객체 인스턴스의 참조(레퍼런스)를 생성자를 통해서 주입(연결)해준다.
- MemberServiceImpl -> MemoryMemberRepositor
- OrderServiceImpl -> MemoryMemberRepository , FixDiscountPolicy
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
- 설계 변경으로 MemberServiceImpl 은 MemoryMemberRepository 를 의존하지 않고, 단지 MemberRepository 인터페이스만 의존한다.
- MemberServiceImpl 입장에서 생성자를 통해 어떤 구현 객체가 들어올지(주입될지)는 알 수 없다.
- MemberServiceImpl 의 생성자를 통해서 어떤 구현 객체를 주입할지는 오직 외부( AppConfig )에서 결정된다.
-
MemberServiceImpl 은 이제부터 의존관계에 대한 고민은 외부에 맡기고 실행에만 집중하면 된다
- appConfig 객체는 memoryMemberRepository 객체를 생성하고 그 참조값을 memberServiceImpl을 생성하면서 생성자로 전달한다.
- 클라이언트인 memberServiceImpl 입장에서 보면 의존관계를 마치 외부에서 주입해주는 것 같다고 해서
DI (Dependency Injection)
우리말로 의존관계 주입 또는 의존성 주입이라 한다
AppConfig 리팩터링
기존 AppConfig
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(new MemoryMemberRepository());
}
public OrderService orderService() {
return new OrderServiceImpl(new MemoryMemberRepository(), new FixDiscountPolicy());
}
}
리팩터링 진행한 AppConfig
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();
}
}
중복을 제거하고 역할에 따른 구현이 보이도록 리팩터링했다. 역할을 세우고 그 안에 구현이 들어가게 해서 애플리케이션 전체 구성이 어떻게 되어있는지 빠르게 파악할 수 있다.