PostCover

디자인 패턴을 쉽게 풀어보자 - 전략 패턴편

GoF의 23가지 디자인 패턴 중 하나인 전략 패턴(Strategy Pattern)을 쉽게 풀어보기

전략(Strategy) 패턴

**전략 패턴(Strategy Pattern)**은 행위를 캡슐화하여 동적으로 변경할 수 있도록 만드는 행동 디자인 패턴입니다. 이 패턴에서는 여러 알고리즘을 각각 클래스로 캡슐화하고, 상황에 따라 유연하게 알고리즘을 교체할 수 있습니다. 객체는 동일한 인터페이스를 사용해 서로 다른 알고리즘을 실행합니다.

문제점: 다양한 알고리즘을 사용하는 코드의 복잡성

예를 들어, 정렬 알고리즘을 여러 방식으로 구현해야 하는 애플리케이션을 생각해봅시다. if-elseswitch 구문으로 알고리즘을 선택하게 되면 코드가 복잡해지고 확장성이 떨어집니다.

class Sorter {

    public void sort(int[] arr, String type) {
        if (type.equals("bubble")) {
            bubbleSort(arr);
        } else if (type.equals("quick")) {
            quickSort(arr);
        }
    }

    private void bubbleSort(int[] arr) {
        System.out.println("Bubble Sort");
        // 버블 정렬 로직...
    }

    private void quickSort(int[] arr) {
        System.out.println("Quick Sort");
        // 퀵 정렬 로직...
    }
}

public class Main {


    public static void main(String[] args) {
        Sorter sorter = new Sorter();
        sorter.sort(new int[]{5, 3, 8, 1}, "quick");
    }
}

문제점

  • 유연성 부족: 새로운 정렬 알고리즘을 추가할 때 기존 코드를 수정해야 합니다.
  • 단일 책임 원칙 위반: Sorter 클래스가 여러 정렬 알고리즘을 모두 관리합니다.
  • 테스트 어려움: 각 알고리즘이 직접 코드에 묶여있어 개별 테스트가 어렵습니다.

해결 방법: 전략 패턴 적용

전략 패턴은 여러 알고리즘을 각각의 클래스로 분리하고, 동일한 인터페이스를 통해 알고리즘을 실행합니다. 이로써 클래스 간의 결합도를 낮추고, 새로운 알고리즘을 쉽게 추가할 수 있습니다.

Strategy 인터페이스 정의

interface SortStrategy {
    void sort(int[] arr);
}

구체적인 알고리즘 클래스 구현

class BubbleSort implements SortStrategy {

    @Override
    public void sort(int[] arr) {
        System.out.println("Performing Bubble Sort");
        // 버블 정렬 로직...
    }
}

class QuickSort implements SortStrategy {

    @Override
    public void sort(int[] arr) {
        System.out.println("Performing Quick Sort");
        // 퀵 정렬 로직...
    }
}

Context 클래스 구현

class Sorter {

    private SortStrategy strategy;

    public void setStrategy(SortStrategy strategy) {
        this.strategy = strategy;
    }

    public void sort(int[] arr) {
        strategy.sort(arr);
    }
}

클라이언트 코드

public class Main {
    public static void main(String[] args) {
        Sorter sorter = new Sorter();

        // 버블 정렬 사용
        sorter.setStrategy(new BubbleSort());
        sorter.sort(new int[]{5, 3, 8, 1});

        // 퀵 정렬 사용
        sorter.setStrategy(new QuickSort());
        sorter.sort(new int[]{5, 3, 8, 1});
    }
}

Strategy 패턴의 동작 원리

  1. Context 객체는 알고리즘을 수행하지만, 실제 알고리즘의 구현은 전략 객체에 위임합니다.
  2. 전략 객체는 공통된 인터페이스를 구현하며, 서로 교체 가능하게 설계됩니다.
  3. 클라이언트는 전략 객체를 동적으로 선택하여 다양한 알고리즘을 유연하게 사용할 수 있습니다.

Strategy 패턴의 장단점

장점

  • 유연한 확장: 새로운 알고리즘을 추가할 때 기존 코드를 수정할 필요가 없습니다.
  • 단일 책임 원칙 준수: 각 알고리즘은 별도의 클래스에 정의되므로 코드의 책임이 분리됩니다.
  • 유연한 교체: 런타임에 알고리즘을 쉽게 교체할 수 있습니다.

단점

  • 클래스 수 증가: 각 알고리즘을 별도의 클래스로 정의해야 하므로 클래스가 많아질 수 있습니다.
  • 전략 선택의 복잡성: 클라이언트가 적절한 전략을 선택해야 합니다.

결론

**전략 패턴(Strategy Pattern)**은 알고리즘을 캡슐화하여 교체 가능하게 설계하는 강력한 패턴입니다. 이 패턴을 사용하면 유연하게 알고리즘을 추가하고 교체할 수 있으며, 코드의 재사용성과 유지보수성이 높아집니다. 정렬 알고리즘, 결제 시스템, 게임 AI 등 동적으로 알고리즘을 변경해야 하는 다양한 상황에서 사용됩니다.

하지만, 클래스 수가 많아질 수 있고 클라이언트가 직접 전략을 선택해야 하므로 복잡도가 증가할 수 있습니다. 따라서 상황에 맞게 적절히 사용하는 것이 중요합니다.