그루비 솔루션의 가장 대표적인 기능은 상품 추천입니다. 고객사가 원하는 추천 알고리즘을 선택하여 캠페인을 생성하면 사용자의 현재 행동 패턴에 따라 그에 알맞은 상품을 추천해주는 기능입니다. 이러한 알고리즘은 각자의 로직이 존재하고 이를 각자의 클래스로 관리한다면 개발자는 해당 알고리즘의 클래스만 확인하면 될 것입니다.
@Service
public class AlgorithmProcess {
...
public List<String> getRecommend() {
...
switch (processType) {
case PR01:
//로직
case PR02:
//로직
case PR03:
//로직
case PR04:
//로직
case PR05:
//로직
...
}
}
...
}
다음과 같은 형태를 가지고 있습니다. 모든 알고리즘을 저렇게 관리하고 있다 보니 보기에도 불편하지 않나요? 문제는 저 이후에도 추천 상품이 가져오지 못하거나 부족할 때 대체 알고리즘 로직을 타게 되는데 대체 알고리즘 로직에서도 저러한 형태를 가지고 있습니다.
저는 하나의 클래스에서 너무 많은 로직을 처리하고 있다는 것이 분명해보였고, 이를 해결하기 위해 팩토리 메서드 패턴을 활용하기로 했습니다.
팩토리 메서드는 부모 클래스에서 객체들을 생성할 수 있는 인터페이스를 제공하지만, 자식 클래스들이 생성될 객체들의 유형을 변경할 수 있도록 하는 생성 패턴입니다.
전체적인 개발 흐름은 다음과 같습니다.
public interface BaseProcess {
String getProcessResult();
ProcessType getProcessType();
}
@Component
public class Process1 implements BaseProcess {
@Override
public String getProcessResult() {
return "프로세스 결과 1";
}
@Override
public ProcessType getProcessType() {
return ProcessType.PR01;
}
}
@Component
public class BaseProcessFactory {
private final Map<ProcessType, BaseProcess> baseProcessMap = new HashMap<>();
public BaseProcessFactory(List<BaseProcess> baseProcessList) {
for(BaseProcess baseProcess : baseProcessList) {
this.baseProcessMap.put(baseProcess.getProcessType(), baseProcess);
}
}
public BaseProcess getBaseProcess(ProcessType processType) {
return baseProcessMap.get(processType);
}
}
@Service
public class ServiceImpl implements Service {
private final BaseProcessFactory baseProcessFactory; // 빈주입
...
public String getProcessResult(ProcessType processType) {
// 팩토리에서 해당 타입의 프로세스 빈의 getProcessResult 실행
return baseProcessFactory.getBaseProcess(processType).getProcessResult
}
...
}
여기서 핵심은 세번째 BaseProcessFactory 클래스입니다. 각각의 프로세스 구현체들은 자신의 프로세스 타입이 무엇인지 리턴해주는 getProcessType 메서드를 가지고 있기때문에 리스트 형태로 받아온 빈들을 Map 에 담아 관리할 수 있게 됩니다.
ServiceImpl 에서 처럼 해당 프로세스 코드만 넘겨주면 해당 프로세스의 빈을 얻어올 수 있게됩니다. 팩토리 메서드 패턴을 사용하고나니 switch문 지옥에서 빠져나온 코드를 확인하실 수 있습니다.
이후 추가적인 프로세스 로직이 생기면 해당 인터페이스를 상속받아 구현만 하면 되고, 수정이 발생 되었을 때도 해당 프로세스 클래스만 수정하면 되기 때문에 유지 보수에도 유리합니다.
썸네일 이미지 및 학습자료 출처
https://refactoring.guru/ko/design-patterns/factory-method