강의를 들으면서 만들었던 예제 중에 다시 짚어볼만 한 것 위주로 정리해보려고 한다.
public interface MemberRepository {
void save(Member member);
Member findById(Long memberId);
}
비지니스 요구사항에서 회원 데이터는 자체 DB를 구축할 수 있고, 외부 시스템과 연동할 수도 있다. 아직 확정이 되지 않았기에, interface를 만들어서 구현체는 언제든지 갈아끼울 수 있도록 한다.
public class MemoryMemberRepository implements MemberRepository{
private static Map<Long, Member> store = new HashMap<>();
@Override
public void save(Member member) {
store.put(member.getId(), member);
}
@Override
public Member findById(Long memberId) {
return store.get(memberId);
}
}
실제 구현체는 이처럼 분리해서 인터페이스를 implements해서 구현한다.
public interface MemberService {
void join(Member member);
Member findMember(Long memberId);
}
public class MemberServiceImpl implements MemberService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
public void join(Member member) {
memberRepository.save(member);
}
public Member findMember(Long memberId) {
return memberRepository.findById(memberId);
}
}
Service도 구현과 역할을 분리한다. 그런데 위 MemberServiceImpl 코드는 문제가 있다. 어떤 설계상 문제가 있을까? 겉보기에는 인터페이스에만 의존해, 역할에 의존한 것처럼 보인다.
private final MemberRepository memberRepository = new MemoryMemberRepository();
📍 하지만 이 부분이 문제다! MemberServiceImpl 안에서
new MemoryMemberRepository();
이 부분을 하면서 직접 MemoryMemberRepository라는 구체 클래스를 new 해주고 있다. 구현과 역할을 분리해서 얻는 이점이 수정과 변경에 용이하다는 것이었는데, 만약 Memory 기반이 아니라 DB를 사용하도록 구현체를 바꾼다면 구현체만 바꿔 끼울 수 있는 게 아니라 저 부분도 바꿔 줘야 한다.
📍 의존 관계가 인터페이스 뿐만 아니라 구현까지 의존하고 있다. 즉, 구현과 역할에 모두 의존한다.