public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
MemberServiceImpl을 보면 private final MemberRepository memberRepository = new MemoryMemberRepository(); 를 통해 추상화와 구현체를 관리하고 있다.
만약 요구사항 변경으로 새로운 구현체로 변경해 줘야 한다면 = new MemoryMemberRepository(); 이 부분에 대한 수정이 필요해진다.
MemberServiceImpl에서 추상화를 통해 구현체를 관리하고 구현체에 대한 변경이 있을 때 인터페이스를 통해서만 구현체를 변경할 수 있어야 하는데 지금 보면 인터페이스가 아닌 MemberServiceImpl에서 코드 수정이 일어난다.
결국 지금 이 코드는 객체 지향 5가지 원칙 중 DIP*(Dependency Inversion Principle | 의존 관계 역전 원칙)*에 위반된다.
public class AppConfig {
public MemberService memberService() {
return new MemberServiceImpl(new MemoryMemberRepository());
}
}
해결을 위해 AppConfig 클래스를 만들어 이런 구현체들을 관리해 준다.
생성과 연결을 담당한다.
return new MemberServiceImpl(new MemoryMemberRepository()); 코드로 구현 객체를 생성한다.
간단히 해석하자면 “MemberServiceImpl을 만들고 내가 만든 MemberServiceImpl은 MemoryMemberRepository를 사용할거야.!” 와 같이 해석할 수 있다.
public class MemberServiceImpl implements MemberService {ㅗ
private final MemberRepository memberRepository;
public MemberServiceImpl(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
public MemberServiceImpl(MemberRepository memberRepository) { this.memberRepository = memberRepository; } 생성자를 통해 받아준다.
이렇게 따로 설정 클래스에서 구현체를 받아 생성자를 통해 추상화와 구체화를 분리해 주는 것을 생성자 주입이라고 한다.
AppConfig 클래스에서는 결국 실제 동작에 필요한 구현 객체를 생성하게 된다.
AppConfig 클래스는 생성한 객체 인스턴스의 참조(레퍼런스)를 생성자를 통해 주입(연결)해준다.
결국 AppConfig 객체는 객체를 생성하고 생성한 객체를 생성자로 전달하는 행위를 하게 되고 이런 행위를 DI(Dependency Injection)라고 한다.( 의존관계 주입)
결과적으로는 MemberServiceImpl 클래스는 인터페이스에만 의존하게 되고 구체적인 구현 클래스에 대해 전혀 의존하지 않게 되므로 DIP를 잘 지키는 코드가 된다.
MemberService memberService;
@BeforeEach
public void beforEach() {
AppConfig appConfig = new AppConfig();
memberService = appConfig.memberService();
}
//MemberService memberService = new MemberServiceImpl(memberRepository);