Strategy
Strategy 패턴은 같은 목적을 달성하는 여러 알고리즘을 각각 별도의 클래스로 분리하고, 사용하는 쪽(Context)이 그 중 하나를 선택해서 실행하는 구조입니다. 알고리즘을 쓰는 코드와 알고리즘 자체를 분리하기 때문에, 새 알고리즘을 추가하거나 기존 것을 바꿀 때 호출하는 쪽을 수정하지 않아도 됩니다.
▶아키텍처 다이어그램
🔍 구조 다이어그램점선 애니메이션은 데이터 또는 요청의 흐름 방향을 나타냅니다
하나의 기능에 조건에 따라 다른 로직을 적용해야 할 때, 가장 자연스러운 첫 시도는 if-else나 switch문입니다. 결제 수단이 3개면 분기도 3개, 검증 규칙이 5종이면 분기도 5개가 됩니다. 여기에 새로운 방식이 추가될 때마다 같은 메서드 안에 분기가 하나씩 늘어나고, 기존 분기와 새 분기가 미묘하게 겹치면서 테스트 범위도 함께 부풀어 갑니다. 분기 하나를 고쳤는데 다른 분기가 깨지는 상황이 반복되면, 문제는 로직의 복잡성이 아니라 로직들이 한곳에 뭉쳐 있다는 구조에 있습니다.
GoF가 Strategy를 패턴으로 정리한 1994년에도 이 문제는 이미 오래된 것이었습니다. 절차적 프로그래밍에서는 함수 포인터로 알고리즘을 교체했고, 객체지향 언어에서는 상속으로 행동을 바꿨습니다. 그런데 상속은 컴파일 시점에 고정되기 때문에 런타임에 알고리즘을 바꾸는 유연함을 주지 못했습니다. Strategy 패턴은 '상속 대신 합성(composition over inheritance)'이라는 설계 원칙의 대표적인 적용입니다. 현대 언어에서는 고차 함수, 클로저, 함수형 인터페이스가 Strategy를 더 가볍게 구현할 수 있게 해줬지만, '알고리즘을 교체 가능한 단위로 분리한다'는 핵심 아이디어는 변하지 않았습니다.
Strategy 패턴에는 세 역할이 있습니다. Strategy 인터페이스는 execute() 같은 공통 메서드를 정의합니다. 구체 Strategy 클래스들은 이 인터페이스를 구현하며 각각 다른 알고리즘을 담습니다. Context는 Strategy 인터페이스 타입의 필드를 갖고, 실행이 필요할 때 그 필드의 메서드를 호출합니다. 핵심은 Context가 구체 Strategy를 모른다는 것입니다. Context는 인터페이스만 의존하기 때문에, 어떤 알고리즘이 들어오든 같은 방식으로 호출합니다. 전략을 바꾸고 싶으면 Context에 다른 Strategy 객체를 주입하면 됩니다. 조건문이 사라지고, 대신 객체 조합으로 행동이 결정됩니다. 주의할 점은 전략이 서로 완전히 독립적이어야 한다는 것입니다. Strategy A의 결과가 Strategy B의 입력에 영향을 주는 식이면 Strategy 패턴이 적합하지 않을 수 있습니다.
Strategy와 State 패턴은 구조가 거의 같습니다. 둘 다 Context가 인터페이스에 실행을 위임하고, 구체 클래스를 교체할 수 있습니다. 차이는 의도에 있습니다. Strategy는 클라이언트가 목적에 맞는 알고리즘을 명시적으로 선택하는 것이고, State는 객체의 내부 상태에 따라 행동이 자동으로 바뀌는 것입니다. Strategy에서는 클라이언트가 어떤 전략을 쓸지 알고 있지만, State에서는 상태 전이가 내부에서 일어나므로 클라이언트가 인지하지 못할 수도 있습니다. Template Method와도 비교됩니다. 둘 다 알고리즘의 변형을 다루지만, Template Method는 상속으로 알고리즘의 일부 단계를 하위 클래스가 재정의하는 방식이고, Strategy는 합성으로 알고리즘 전체를 외부 객체에 위임하는 방식입니다. 런타임 교체가 필요하면 Strategy, 알고리즘 골격은 고정하고 세부 단계만 바꾸고 싶으면 Template Method가 맞습니다.
자주 비교하는 개념
Template Method
알고리즘의 골격은 고정하고 세부 단계만 서브클래스에 맡기는 패턴
Strategy는 합성으로 알고리즘 전체를 교체하고, Template Method는 상속으로 알고리즘의 특정 단계만 재정의합니다. 교체 단위와 교체 시점이 핵심 차이입니다.
Command
요청을 객체로 캡슐화해 실행·취소·큐잉을 가능하게 하는 행동 패턴
Strategy는 '어떻게 수행할지'를 캡슐화하고, Command는 '무엇을 수행할지'를 캡슐화합니다. Strategy는 알고리즘 교체가 목적이고 Command는 요청의 객체화가 목적입니다.
Observer
상태 변경을 구독자에게 자동으로 전파하는 행동 패턴
Strategy는 알고리즘 교체가 목적이고, Observer는 상태 변경의 전파가 목적입니다. 둘 다 인터페이스로 결합을 끊지만 해결하는 문제 영역이 다릅니다.
Iterator
컬렉션의 내부 구조를 감추고 요소를 순서대로 탐색하는 패턴
Iterator는 순회 알고리즘만 교체하는 특수한 형태이고, Strategy는 임의의 알고리즘을 교체합니다. Iterator를 순회에 특화된 Strategy로 볼 수 있습니다.
Strategy 패턴은 '같은 일을 다르게 처리하는 방식이 2개 이상이고, 앞으로 더 늘어날 수 있는가'라는 질문에 답이 예일 때 도입합니다. 결제 시스템에서 카드, 계좌이체, 간편결제 각각의 처리 로직을 Strategy로 분리하면, 새로운 결제 수단이 추가돼도 기존 결제 흐름 코드를 건드리지 않습니다. 설정이나 사용자 선택에 따라 런타임에 행동이 바뀌어야 하는 경우에도 적합합니다. 파일 내보내기에서 CSV, JSON, XML 포맷을 지원할 때, 각 포맷을 Strategy로 만들면 Context는 포맷에 무관하게 '내보내기'만 호출합니다. 반대로 알고리즘이 하나뿐이거나 변할 가능성이 낮다면 Strategy 패턴은 불필요한 추상화입니다. 분기가 2개인데 Strategy를 도입하면 클래스 수만 늘어나고 코드를 읽기 어려워질 수 있습니다. 패턴의 도입 시점은 '지금 분기가 몇 개인가'보다 '이 분기가 독립적으로 늘어날 축인가'로 판단하는 편이 낫습니다.