팩토리 메소드 패턴

자바(JAVA) - 팩토리 메소드 패턴(Factory Method Pattern)

팩토리 메소드 패턴은 제 블로그에 정리되어있습니다.

패턴 적용의 이유

로그인에 접근하는 외부 API는 언제든지 변경될 수 있습니다. 작성기준 현재 소셜로그인은 카카오 API만 존재합니다. 하지만 이후에 네이버, 깃, 구글 등 여러 외부 API로 연동한다고하면 검증부터 구현까지 다시 만들어야하는 불편함이 있습니다. 또한 로그인 로직에 대한 일관성도 깨질 수 있는만큼 확장성을 생각하여 디자인패턴 - 생성패턴에 팩토리 메소드 패턴을 구현했습니다.

패턴의 구현

public interface MemberFactory {

    MemberInterface createMember();
    MemberValidator validator();

    @Transactional
    default Member signup(RequestSignupDto signupDto) {
        MemberInterface memberInterface = createMember();

        MemberValidator validator = validator();
        validator.validSignup(signupDto);

        return memberInterface.signup(signupDto);
    }

    @Transactional
    default Member login(RequestLoginDto loginDto, HttpSession session, HttpServletResponse response) {
        MemberInterface memberInterface = createMember();
        Member member = memberInterface.login(loginDto);

        MemberValidator validator = validator();
        validator.validLogin(member, response);

        if (member != null) {
            session.setAttribute(LOGIN_MEMBER, member.getMemberId());
        }

        return member;
    }

}

팩토리 인터페이스를 구현하고 createMembervalidator를 구현체에 오버라이드 시킵니다.

그리고 실제 로그인과 회원가입 로직을 인터페이스의 default 로 구현하여 모든 외부 API와 일반로직을 통합시킵니다.

따라서 createMember와 validator를 하위 구현체에서 구현하기만 하면 현재 코드에 손대지 않고 손쉽게 외부 API를 확장할 수 있습니다.

즉 수정에는 닫혀있고, 확장에는 열려있는 OCP원칙을 지킬 수 있고, 각 API에 대한 구현체가 생성되기 때문에 단일책임원칙도 지킬 수 있게 되었습니다.

@RequiredArgsConstructor
public class DefaultMemberFactory implements MemberFactory{

    private final BasicJpaRepository basicJpaRepository;
    private final MemberJpaRepository memberJpaRepository;
    private final FreezeRepository freezeRepository;
    private final BCryptPasswordEncoder encoder;

    @Override
    public MemberInterface createMember() {
        return new DefaultMember(basicJpaRepository, memberJpaRepository, encoder);
    }

    @Override
    public MemberValidator validator() {
        return new DefaultMemberValidator(freezeRepository, basicJpaRepository, memberJpaRepository);
    }
}

@RequiredArgsConstructor
public class KakaoMemberFactory implements MemberFactory{

    private final FreezeRepository freezeRepository;
    private final KakaoLoginRepository kakaoLoginRepository;
    private final MemberJpaRepository memberJpaRepository;
    private final SocialJpaRepository socialJpaRepository;
    private final SocialTokenJpaRepository socialTokenJpaRepository;
    private final PhoneJpaRepository phoneJpaRepository;
    @Override
    public MemberInterface createMember() {
        return new KakaoMember(kakaoLoginRepository, memberJpaRepository, socialJpaRepository, socialTokenJpaRepository, phoneJpaRepository);
    }

    @Override
    public MemberValidator validator() {
        return new SocialMemberValidator(freezeRepository, socialJpaRepository, memberJpaRepository);
    }
}