스프링에서 Service
를 두고 ServiceImpl
로 이를 구현하는 경우를 볼 수 있다.
객체 간의 결합도를 낮추어 변화에 유연한 개발을 하기 위해서이다. 하나의 인터페이스를 구현하는 여러 구현체가 있고 기능에 따라 적절한 구현체가 들어가서 다형성을 주기 위함이다. 또 하나의 인터페이스만 바라보니 의존관계도 줄일 수 있다.
JDK Dynamic Proxy
는 인터페이스 기반으로 프록시 객체를 만들게 되어 있다. 예를 들어, 인터페이스가 있어야지 @Transactional
어노테이션이 동작하게 된다.
하지만 특정 버전부터 CGLIB
라이브러리를 사용하여, 클래스 기반으로 AOP Proxy
를 만들도록 지원을 하게 되었다. 그래서 개발자는 AOP Proxy
를 만드는 방식을 선택할 수 있게 되었다.
// 비밀번호를 바꾸는 인터페이스
public interface ChangePasswordService {
public void change(MemberId id, PasswordDto.ChangeRequest dto);
}
// 비밀번호 기반으로 비밀번호를 변경하는 기능
public class ByPasswordChangePasswordService implements ChangePasswordService {
private MemberFindService memberFindService;
@Override
public void change(MemberId id, PasswordDto.ChangeRequest dto) {
if (dto.getPassword().equals("비밀번호가 일치하는지 판단 로직...")) {
final Member member = memberFindService.findById(id);
final String newPassword = dto.getNewPassword().getValue();
member.changePassword(newPassword);
}
}
}
// 비밀번호를 잃어버렸을 때 다른 인증 기반으로 비밀번호를 변경하는 기능
public class ByAuthChangePasswordService implements ChangePasswordService {
private MemberFindService memberFindService;
@Override
public void change(MemberId id, PasswordDto.ChangeRequest dto) {
if (dto.getAuthCode().equals("인증 코드가 적합한지 로직 추가...")) {
final Member member = memberFindService.findById(id);
final String newPassword = dto.getNewPassword().getValue();
member.changePassword(newPassword);
// 필요로직...
}
}
}
예를 들어 ChangePasswordService
인터페이스의 책임은 비밀번호를 변경하는 것이다. 해당 구현체들은 비밀번호 변경에 대한 다양한 로직을 구현하게 된다. 이것을 인터페이스로 둘 수 있는 이유는 인터페이스의 책임이 하나이기 때문이다.