이전 포스팅에서 작성했듯, 다형성만으로는 역할과 구현의 구분이 이루어지지 않는다.
public class MemberServiceImpl implements MemberService{
private final MemberRepository memberRepository = new MemoryMemberRepository();
MemberRepository 라는 인터페이스의 구현체로 MemoryMemberRepository와 JdbcMemberRepository를 만들었다고 가정하자. 각각 로컬에 저장하는 경우, DB를 사용하는 경우에 기능한다.
다형성에 따라 어떤 저장소를 사용할 지를 바꾸어줄 수 있으며, 다른 구현체로 바꾸더라도 동작에 문제가 생기지 않을 것이다. (같은 인터페이스의 구현체이므로)
앞선 코드의 MemberServiceImpl은 MemberRepository 인터페이스(역할)에 의존함과 동시에, MemoryMemberRespository 구현체 (구현)에 의존한다. 이는 SOLID에 위배되는 형태이다.
MemberServiceImpl 클래스 내의 관심사의 분리가 이루어지지 않고 있다. 역할과 구현의 예시로 본다면, 뮤지컬 로미오의 역할(MemberService)을 맡은 배우(MemberServiceImpl)가 뮤지컬을 함과 동시에, 쥴리엣 역할(MemberRepository)의 배우(MemoryMemberRespotiory)를 결정해야 하는 격이다.
배우가 뮤지컬만 할 수 있도록, 관심사를 분리하는 과정이 필요하다. 배우를 결정하는 것은 기획자의 역할로서, AppConfig가 맡아주어야 한다.
AppConfig의 코드는 아래와 같다.
public class AppConfig {
public MemberService memberService(){
return new MemberServiceImpl(new MemoryMemberRepository());
}
public OrderService orderService(){
return new OrderServiceImpl(new RateDiscountPolicy(), new MemoryMemberRepository());
}
}
다른 class에서 MemberService 메서드를 호출하면 -> MemberServiceImpl 구현체를 리턴함과 동시에, MemoryMemberRepository 객체를 생성해 파라미터로 보낸다
public class MemberServiceImpl implements MemberService{
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
MemberServiceImpl에서는 생성자에 따라 MemberRepository의 구현체로 MemoryMemberRepository를 받게 된다.
이 때 MemberServiceImpl의 입장에서는, 외부에서 의존성을 주입해주는 것이기 때문에 의존성 주입(DI)라고 부른다.
//기존 코드
private final MemberRepository memberRepository = new MemoryMemberRepository();
//의존성 주입
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
기존 코드에서는 해당 클래스가 직접 MemoryMemberRepository 구현체를 결정해야 했으나 <-> 의존성 주입을 통해, 구현체가 생성될 때 필요한 MemoryMemberRepository를 주입받는다.
이제는 MemberRepository 인터페이스에만 의존한다.
만약 MemoryMemberRepository를 JdbcMemberRepository로 바꾼다면, AppConfig의
public MemberService memberService(){
return new MemberServiceImpl(new MemoryMemberRepository());
} 부분만 변경하면 된다. 변경에 닫혀있게 된다.
AppConfig가 MemberServiceImpl 클래스에 의존성을 주입하므로, 구현체를 결정할 필요 없이, 실행만 하면 된다!
기존 AppConfig의 코드는 역할이 제대로 보이지 않았다.
public class AppConfig {
public MemberService memberService(){
return new MemberServiceImpl(new MemoryMemberRepository());
}
public OrderService orderService(){
return new OrderServiceImpl(new RateDiscountPolicy(), new MemoryMemberRepository());
}
}
public class AppConfig {
public MemberService memberService(){
return new MemberServiceImpl(memberRepository());
}
private static MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
public OrderService orderService(){
return new OrderServiceImpl(discountPolicy(), memberRepository());
}
private static DiscountPolicy discountPolicy() {
return new RateDiscountPolicy();
}
}
파라미터로 있던 객체 생성 코드를 새로운 메소드로 생성하였다.
이전에 강의를 들었을 땐 DI의 개념이 굉장히 어렴풋이 이해가 갔는데, 다시 강의를 듣고, 오늘 발 들이기 시리즈로 정리하며 비로소 내 것이 된 기분이다. 왜 다형성만으로는 역할과 구현이 완전히 구분될 수 없는지에 대해서!
지금 JAVA 코드로도 충분하다고 생각이 드는데, Spring이 도입됨에 따라 어떤 부분이 변경되고 편리해질지 궁금하다.