네비게이션 앱에서 경로를 탐색할 때 "자동차", "대중교통", "자전거" 중 하나를 고른다. 목적지는 같지만 경로 탐색 알고리즘은 완전히 다르다. 그리고 언제든지 바꿀 수 있다.
Strategy 패턴은 이처럼 알고리즘을 교체 가능한 단위로 캡슐화하는 패턴이다.
알고리즘(전략)을 인터페이스로 정의하고, 각 구현체를 별도 클래스로 분리한다. 컨텍스트 객체는 전략 객체를 주입받아 실행하기 때문에, 알고리즘을 런타임에 자유롭게 교체할 수 있다.

// 전략 인터페이스
public interface SortStrategy {
void sort(int[] arr);
}
// 구체 전략들
public class BubbleSort implements SortStrategy {
@Override
public void sort(int[] arr) {
System.out.println("버블 정렬 실행");
// 버블 정렬 로직
}
}
public class QuickSort implements SortStrategy {
@Override
public void sort(int[] arr) {
System.out.println("퀵 정렬 실행");
// 퀵 정렬 로직
}
}
public class MergeSort implements SortStrategy {
@Override
public void sort(int[] arr) {
System.out.println("병합 정렬 실행");
// 병합 정렬 로직
}
}
// 컨텍스트 — 전략을 주입받아 실행
public class Sorter {
private SortStrategy strategy;
public Sorter(SortStrategy strategy) {
this.strategy = strategy;
}
// 런타임에 전략 교체 가능
public void setStrategy(SortStrategy strategy) {
this.strategy = strategy;
}
public void sort(int[] arr) {
strategy.sort(arr);
}
}
int[] data = {5, 3, 8, 1, 9};
Sorter sorter = new Sorter(new BubbleSort());
sorter.sort(data); // 버블 정렬 실행
// 런타임에 전략 교체
sorter.setStrategy(new QuickSort());
sorter.sort(data); // 퀵 정렬 실행
Sorter는 정렬 알고리즘이 어떻게 동작하는지 전혀 모른다. 전략 객체에게 위임할 뿐이다.
Strategy 없이 구현하면 이런 코드가 된다.
public void sort(int[] arr, String type) {
if (type.equals("bubble")) {
// 버블 정렬 로직
} else if (type.equals("quick")) {
// 퀵 정렬 로직
} else if (type.equals("merge")) {
// 병합 정렬 로직
}
// 새 알고리즘이 생길 때마다 이 메서드를 수정해야 함
}
새 정렬 방법이 추가될 때마다 기존 코드를 열어 else if를 추가해야 한다. Strategy를 쓰면 새 전략 클래스만 추가하면 되고, 기존 코드는 건드리지 않는다.
Java 8부터는 함수형 인터페이스와 람다를 활용해 전략 객체를 더 간결하게 전달할 수 있다.
// 전략 인터페이스가 함수형 인터페이스라면
@FunctionalInterface
public interface SortStrategy {
void sort(int[] arr);
}
// 람다로 전략 전달
Sorter sorter = new Sorter(arr -> System.out.println("람다 정렬 실행"));
별도 클래스를 만들지 않아도 되니, 간단한 전략에는 람다가 더 편할 수 있다.
if-else, switch)로 알고리즘을 선택하는 코드가 비대해질 때Strategy의 핵심은 "무엇을 할지"와 "어떻게 할지"를 분리하는 것이다. 컨텍스트는 무엇을 할지 알고, 어떻게 할지는 전략에게 맡긴다.