Spring
Spring Boot에서 전략 패턴 사용하기
웅둘
2024. 3. 29. 16:59
전략패턴
- 전략 패턴은 런타임 중에 알고리즘 전략을 선택하여 객체 동작을 실시간으로 바뀌도록 할 수 있게 하는 행위 디자인 패턴
- 어떤 일을 수행하는 알고리즘이 여러가지 일때, 동작들을 미리 전략으로 정의함으로써 손쉽게 전략을 교체할 수 있는, 알고리즘 변형이 빈번하게 필요한 경우 적합한 패턴이다.
첫 번째 시도
우선 전략 패턴을 위한 인터페이스와 enum을 작성하였다.
public interface Strategy {
void execute();
}
@Getter
@RequiredArgsConstructor
public enum StrategyType {
A("strategyA"), B("strategyB"), C("strategyC")
private final strategyName;
}
전략 패턴을 위한 A,B,C 알고리즘을 작성
@Component
public class StrategyA implements Strategy {
@Override
public void execute() {
// A 실행
}
}
@Component
public class StrategyB implements Strategy {
@Override
public void execute() {
// B 실행
}
}
@Component
public class StrategyC implements Strategy {
@Override
public void execute() {
// C 실행
}
}
StrategyFactory을 만들고, 모든 전략들을 Map을 이용해서 저장한다.
@Component
@RequiredArgsConstructor
public class StrategyFactory {
private final Map<String, Strategy> strategyMap;
public void execute(StrategyType strategyType) {
Strategy strategy = strategyMap.get(strategyType.getStrategyName());
strategy.excute();
}
}
Map<String, Strategy>
- Key : 스프링 빈의 이름
- Value : 스프링 빈
Map에 전략들이 저장되어 있기 때문에 StrategyType에 따라 로직을 실행할 시킬 수 있다.
문제점
각 전략에 대한 스프링 빈의 이름을 Enum StrategyType의 strategyName 이름을 직접 넣고 있다.
그렇기 때문에 전략을 구현한 클래스의 이름이 변경되어도 컴파일 에러가 나지 않기 때문에 문제를 일으킬 것이라 생각했다.
두 번째 시도
public interface Strategy {
void execute();
boolean isStrategyType(StrategyType strategyType);
}
public enum StrategyType {
A, B, C
}
- enum의 strategyName을 제거
- 전략 패턴 구현 클래스는 동일
@Component
public class StrategyA implements Strategy {
@Override
public void execute() {
// A 실행
}
@Override
public boolean isStrategyType(StrategyType strategyType) {
return strategyType == StrategyType.A;
}
}
@Component
public class StrategyB implements Strategy {
@Override
public void execute() {
// B 실행
}
@Override
public boolean isStrategyType(StrategyType strategyType) {
return strategyType == StrategyType.B;
}
}
@Component
public class StrategyC implements Strategy {
@Override
public void execute() {
// C 실행
}
@Override
public boolean isStrategyType(StrategyType strategyType) {
return strategyType == StrategyType.C;
}
}
- boolean isStrategyType 구현
@Component
@RequiredArgsConstructor
public class StrategyFactory {
private final List<Strategy> strategies;
public void execute(StrategyType strategyType) {
Strategy strategy = strategyMap.getStrategy(strategyType.getStrategyName());
strategy.excute();
}
private Strategy getStrategy(StrategyType strategyType) {
return strategies.stream()
.filter(strategy -> strategy.isStrategyType(strategyType))
.findAny()
.orElseThrow(() -> new IllegalArgumentException("오류"));
}
}
- List로 만들고 반복문을 통해 입력 받은 전략과 일치하는 전략 알고리즘을 사용하였다.
문제점
첫 번째 시도에서 Map을 사용하였고 전략을 가져오는 시간이 O(1) 이였다.
전략에 대한 구현 클래스가 많지는 않겠지만, List로 변경됨으로써 전략을 가져오는 시간이 O(n)이 되어버린게 다소 찝찝하다.
세 번째 시도
public interface Strategy {
void execute();
StrategyType getStrategyType();
}
public enum StrategyType {
A, B, C
}
- getStrategyType 메소드 추가
@Component
public class StrategyA implements Strategy {
@Override
public void execute() {
// A 실행
}
@Override
public StrategyType getStrategyType() {
return StrategyType.A;
}
}
@Component
public class StrategyB implements Strategy {
@Override
public void execute() {
// B 실행
}
@Override
public StrategyType getStrategyType() {
return StrategyType.B;
}
}
@Component
public class StrategyC implements Strategy {
@Override
public void execute() {
// C 실행
}
@Override
public StrategyType getStrategyType() {
return StrategyType.C;
}
}
@Component
public class StrategyFactory {
private final Map<StrategyType, Strategy> strategyMap;
@Autowired
public StrategyFactory(Set<Strategy> StrategySet) {
createStrategy(StrategySet);
}
private void createStrategy(Set<Strategy> StrategySet) {
strategyMap= new HashMap<>();
strategyMap.forEach(
strategy-> strategyMap.put(StrategySet.getStrategyType(), strategy)
);
}
public Strategy findStrategy(Strategy strategy) {
return strategyMap.get(strategy);
}
}
@Service
@RequiredArgsConstructor
public class DomainService {
private StrategyFactory strategyFactory;
public void execute(StrategyType strategyType) {
Strategy strategy = strategyFactory.findStrategy(StrategyName.StrategyA);
strategy.execute();
}
}
첫 번째 방식에서 전략을 담은 자료구조 Map과
두 번째 방식에서 전략을 찾기 위해 사용했던 메소드 방식을 합쳐서 나온 결과이다.
마지막 방식은 각각의 방식에서 사용했던 문제점을 해결한 방식인 것 같아 만족스러웠다.