[디자인패턴] 전략 패턴 (Strategy Pattern)

koline·2023년 9월 11일
0

디자인패턴

목록 보기
22/24
post-thumbnail

전략 패턴


알고리즘 군을 정의하고(추상 클래스) 같은 알고리즘을 각각 하나의 클래스로 캡슐화한 다음, 필요할 때 서로 교환해서 사용할 수 있게 하는 패턴으로, 행위를 클래스로 캡슐화해 동적으로 행위를 자유롭게 바꿀 수 있게 해주는 패턴이다.

즉, 어떤 일을 수행하는 알고리즘이 여러가지 일때, 동작들을 미리 전략으로 정의함으로써 런타임중에 손쉽게 전략을 교체할 수 있는, 알고리즘 변형이 빈번하게 필요한 경우에 적합한 패턴이다.



구조


  1. Strategy(전략 인터페이스) : 모든 전략 구현제에 대한 공용 인터페이스
  2. ConcreteStrategy(전략 알고리즘 객체) : 알고리즘, 행위, 동작을 객체로 정의한 구현체
  3. Context(컨텍스트) : 알고리즘을 실행해야 할 때마다 해당 알고리즘과 연결된 전략 객체의 메소드를 호출.



구현


// MoveStrategy.java (Strategy)
public interface MoveStrategy {
    void move();
}

// Run.java (ConcreteStrategy)
public class Run implements MoveStrategy {
    @Override
    public void move() {
        System.out.println("[Run] Robot is running...");
    }
}

// Walk.java (ConcreteStrategy)
public class Walk implements MoveStrategy {
    @Override
    public void move() {
        System.out.println("[Walk] Robot is walking...");
    }
}

// Robot.java (Context)
public class Robot {
    private MoveStrategy moveStrategy;

    public Robot(MoveStrategy moveStrategy) {
        this.moveStrategy = moveStrategy;
    }

    public void setMoveStrategy(MoveStrategy moveStrategy) {
        this.moveStrategy = moveStrategy;
    }

    public void move() {
        moveStrategy.move();
    }
}

// Client.java (Client)
public class Client {
    public static void main(String[] args) {
        Robot robot = new Robot(new Run());

        robot.move();

        robot.setMoveStrategy(new Walk());
        robot.move();
    }
}

// 실행 결과
[Run] Robot is running...
[Walk] Robot is walking...

위 예제에서 로봇이라는 객체의 이동방식을 MoveStrategy라는 인터페이스의 move 메소드를 사용해 정의하고 하위에 Run과 Walk라는 클래스에 move 메소드를 각각 구현했다. 즉, 이동이라는 행위를 클래스로 구현하고 Robot의 속성으로서 가지고 있다가 이동 시에 move 메소드가 실행이 되는 것이다. 전략 패턴의 장점은 확장에도 용이하다는 것이다. 만약 여기서 로봇이 말을 할 수 있다면, 그리고 한국어와 영어를 구사할 수 있다면 아래와 같이 구현할 수 있다.

// LanguageStrategy.java (Strategy)
public interface LanguageStrategy {
    void speak();
}

// Korean.java (ConcreteStrategy)
public class Korean implements LanguageStrategy {
    @Override
    public void speak() {
        System.out.println("[Korean] Robot speaks in Korean...");
    }
}

// English.java (ConcreteStrategy)
public class English implements LanguageStrategy {
    @Override
    public void speak() {
        System.out.println("[English] Robot speaks in English...");
    }
}

// Robot.java (Context)
public class Robot {
    private MoveStrategy moveStrategy;
    private LanguageStrategy languageStrategy;

    public Robot(MoveStrategy moveStrategy, LanguageStrategy languageStrategy) {
        this.moveStrategy = moveStrategy;
        this.languageStrategy = languageStrategy;
    }

    public void setMoveStrategy(MoveStrategy moveStrategy) {
        this.moveStrategy = moveStrategy;
    }

    public void move() {
        moveStrategy.move();
    }

    public void setLanguageStrategy(LanguageStrategy languageStrategy) {
        this.languageStrategy = languageStrategy;
    }

    public void speak() {
        languageStrategy.speak();
    }
}

// Client.java (Client)
public class Client {
    public static void main(String[] args) {
        Robot robot = new Robot(new Run(), new Korean());

        robot.move();
        robot.speak();

        robot.setMoveStrategy(new Walk());
        robot.move();

        robot.setLanguageStrategy(new English());
        robot.speak();
    }
}

// 실행 결과
[Run] Robot is running...
[Korean] Robot speaks in Korean...
[Walk] Robot is walking...
[English] Robot speaks in English...

위의 언어설정과 말하기 기능이 구현된 클래스들을 생성한 후 Robot 클래스에 언어설정 속성을 추가해 준다. 이렇게 간단하게 새로운 기능을 추가할 수 있다. 만약 언어의 종류나 이동의 종류가 많아지는 경우에는 그저 Strategy를 implement하는 구현 클래스만 추가해주면 되기 때문에 매우 간편하다.

만약 전략 패턴을 사용하지 않고 위와 동일한 모듈을 구현한 후 새로운 기능을 추가하려고 한다면 예컨데 Robot을 인터페이스로 구현한 후 해당 인터페이스를 상속받는 WalkingRobot과 RunningRobot으로 구현했을 것이고, 거기에 말하기 기능을 추가하려면 KoreanWalkingRobot, KoreanRunningRobot, EnglishWalkingRobot, EnglishRunningRobot 으로 구현해야 했을 것이다. 이 방식은 기능이 추가될수록 구현해야 하는 클래스가 기하급수적으로 많아지고, 변경 시 기존 클래스를 수정해야 한다는 점에서 객체지향 설계원칙의 OCP(개방 폐쇄의 원칙)에 어긋난다.



사용 목적

  1. 전략 알고리즘의 여러 버전 또는 변형이 필요할 때 클래스화를 통해 관리
  2. 알고리즘 코드가 노출되어서는 안 되는 데이터에 액세스 하거나 데이터를 활용할 때 (캡슐화)
  3. 알고리즘의 동작이 런타임에 실시간으로 교체 되어야 할때

주의점

  1. 알고리즘이 많아질수록 관리해야할 객체의 수가 늘어난다는 단점이 있다.
  2. 만일 어플리케이션 특성이 알고리즘이 많지 않고 자주 변경되지 않는다면, 새로운 클래스와 인터페이스를 만들어 프로그램을 복잡하게 만들 이유가 없다.
  3. 개발자는 적절한 전략을 선택하기 위해 전략 간의 차이점을 파악하고 있어야 한다. (복잡도 ↑)



참고


[디자인패턴] 디자인패턴이란? - 생성패턴, 구조패턴, 행위패턴

전략(Strategy) 패턴 - 완벽 마스터하기

profile
개발공부를해보자

0개의 댓글