전략 패턴을 사용하기 위해 Selector 클래스에 여러가지 의존관계를 추가해주는 것은 유지 보수, 클린 코드 측면에서 아주아주 아주 X5 안 좋은 생각이다.
즉, selector에서는 전략들이 어떤 전략이 있는지 알면 안된다.(사실 selector도 필요 없다.)
아래의 내용은 스프링에서와 자바 내부 라이브러리만을 활용한 방법으로(2가지) 나뉜다.
우선, 스프링 환경에서이다.
1. 스프링 환경에서 전략 패턴 활용
1-1. list를 활용한 전략 패턴
-이 방법은 각 전략들이 자신이 어떤 전략인지 뽐낸다(??)는 생각에서 시작한다.
-각 객체들이 이럴때, 나를 써라!! 라는 든든한(?) supports라는 메서드를 갖는다.
-특정 인터페이스를 구현한 구현체들을 모두 빈으로 주입받아, for문을 돌려 support를 사용한다.
-만약 support가 true라면 그걸 return 한다.
public interface CalculationStrategy {
int calculate(String input);
boolean supports(String input);
}
@Component
public class DefaultDelimiterCalculationStrategy implements CalculationStrategy {
@Override
public int calculate(String input) {
System.out.println("Using default delimiter strategy");
return 0;
}
@Override
public boolean supports(String input) {
// 기본 구분자는 커스텀 구분자가 아닌 경우에 내가 쓰인다!!
return !input.startsWith("//");
}
}
@Component
public class CustomDelimiterCalculationStrategy implements CalculationStrategy {
@Override
public int calculate(String input) {
System.out.println("Using custom delimiter strategy");
return 0;
}
@Override
public boolean supports(String input) {
// 커스텀 구분자를 사용하는 경우에 내가 쓰인다!!
return input.startsWith("//");
}
}
@Component
public class CalculationStrategySelector {
private final List<CalculationStrategy> strategies;
@Autowired
public CalculationStrategySelector(List<CalculationStrategy> strategies) {
this.strategies = strategies;
}
public CalculationStrategy selectStrategy(String input) {
for (CalculationStrategy strategy : strategies) {
if (strategy.supports(input)) {
return strategy; // 조건에 맞는 전략 반환
}
}
throw new IllegalArgumentException("No suitable strategy found for input: " + input);
}
}
@Component
public class CalculationStrategyExecutor {
private final CalculationStrategySelector strategySelector;
@Autowired
public CalculationStrategyExecutor(CalculationStrategySelector strategySelector) {
this.strategySelector = strategySelector;
}
public void executeStrategy(String input) {
CalculationStrategy strategy = strategySelector.selectStrategy(input);
strategy.calculate(input);
}
}
위의 코드를 보면 전략들을 가진 List(체인이라고도 볼 수 있다.)를 갖고
stream으로 돌려 input에 따른 전략을 선택해 실행하고 있다.
체인을 돌려 특정 조건에 맞춰지면 return한다라...
체인...
ㅊ[인......
책인..........
책임....???
그렇다 책임 연쇄 패턴과 유사한 디자인 패턴이 되었다.
chain(필터)를 돌려, 조건에 맞으면 자신의 메서드를 실행한다는 스프링 시큐리티의 논리와 비슷해 보인다.
즉, 유지 보수를 위해서 책임 연쇄 패턴과 유사하게끔 만들어 준 것이다.
다시말해,
1. 자기 자신을 이때 사용해라 라는 설명서를 가진 구현체들을 만든다.
2. selector에서 모든 객체들을 list에 담는다.(추가/삭제해도 어차피 상관없다. list에 아무도 모르게 추가/삭제되기 때문)
3. selector는 support를 이용할 수 있게 데이터를 잘 가공해 list를 돌린다.
4. 발견된 전략을 실행한다.
이렇게 된다.
2. 스프링을 활용하지 않는 책임 연쇄 패턴에 유사한 전략 패턴(사실 어떤 패턴이라 불러야할 지를 모르겠다...)
방법은 간단하다. 스프링을 사용하지 않는다면, 문제가 되는 것은 의존 주입이다. 즉, List에 모든 전략들을 한 번에 담을 수 있는 클래스를 구분하면 되는 일이다.
== Factory클래스를 쓰면 되겠다.
public class CalculationStrategyFactory {
private final List<CalculationStrategy> strategies;
public CalculationStrategyFactory() {
// 전략들을 생성하고 리스트에 추가
strategies = new ArrayList<>();
strategies.add(new DefaultDelimiterCalculationStrategy());
strategies.add(new CustomDelimiterCalculationStrategy());
}
public List<CalculationStrategy> getStrategies() {
return strategies;
}
}
public class CalculationStrategySelector {
private final List<CalculationStrategy> strategies;
public CalculationStrategySelector(CalculationStrategyFactory factory) {
this.strategies = factory.getStrategies();
}
public CalculationStrategy selectStrategy(String input) {
for (CalculationStrategy strategy : strategies) {
if (strategy.supports(input)) {
return strategy; // 조건에 맞는 전략 반환
}
}
throw new IllegalArgumentException("No suitable strategy found for input: " + input);
}
}
이제, 의존성들은 config에서 넣어주면 된다.
완성이다. 정말 쉽다.
'디자인 패턴' 카테고리의 다른 글
디자인 패턴 예시와 공부 기록 (0) | 2025.01.23 |
---|---|
형 변환을 이용한 ISP (0) | 2024.10.23 |