Spring/Spring

[디자인 패턴] 팩토리 패턴 모범 사례 학습

Jemlog 2023. 1. 15. 12:24

인턴을 진행하면서 실제 실무에서 사용하는 코드를 접할 수 있는 기회를 가지게 됐다. 객체 지향의 장점을 살리기 위한 다양한 디자인 패턴들이 코드에 자연스럽게 녹아있는걸 알 수 있었다. 최근 클린 아키텍처에 대한 관심이 커져서 패키지 구조나 계층 간 매핑 전략 등에 신경을 많이 쓰고 있었지만, 이번을 계기로 객체 지향의 특징을 잘 지키는 코드를 작성하는 방법에 대한 고민을 많이 하게 되었다.

 

그러던 중, 망나니 개발자님의 블로그 글 중에 팩토리 패턴에 대한 글을 발견했고, 앞으로 팀 프로젝트나 실무에서 많이 사용할만한 패턴인 것 같아서 블로그에 정리를 하려고 한다.

 

// 구체 클래스들이 구현하게 될 LoginService 인터페이스이다.
public interface LoginService {

    // if-else문을 통해서 분기처리를 하는 대신, 지원 여부를 구체 클래스 내에서 진행한다.
    boolean supports(LoginType loginType);

    void login();
}
public class WebLogin implements LoginService{

    // 다양한 로그인 방식이 추가될때마다 LoginService를 구현해서 만들기만 하면 된다. 
    // 외부적으로 추가되는 코드는 하나도 없다.
    @Override
    public boolean supports(LoginType loginType) {
        return loginType == LoginType.WEB;
    }

    @Override
    public void login() {
        System.out.println("web login!");
    }
}
@RequiredArgsConstructor
public class LoginFactory {

    private final List<LoginService> loginServiceList;
    
    // 이미 생성된 객체를 재활용할 수 있게 해주는 캐시
    private final Map<LoginType,LoginService> factoryCache;

    /**
     * 팩토리에서 if-else를 사용해서 특정 타입을 선택하는게 아니라, 타입 판별을 구체 클래스 자체에서 처리
     */
    public LoginService find(final LoginType loginType)
    {
        // 먼저 캐시에 해당 Service가 있는지 체크
        LoginService loginService = factoryCache.get(loginType);
        
        // 만약 있다면 추가적인 과정 없이 바로 반환
        if(loginService != null)
        {
            return loginService;
        }
        
        // 전체 리스트를 순회하면서 타입에 맞는 객체를 찾음
        loginService = loginServiceList.stream()
                .filter(v -> v.supports(loginType))
                .findFirst()
                .orElseThrow();

        // 찾은 객체를 캐시에 넣음
        factoryCache.put(loginType,loginService);

        return loginService;
    }
}