Design Pattern, Strategy

2021. 12. 29. 18:11ETC/Design Patterns

 

Behavioral Object Pattern

Strategy Pattern

 

Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it.

- GoF Design Patterns

 

전략 패턴은 동일 계열의 알고리즘군을 정의하고, 각 알고리즘을 캡슐화하며, 이들을 상호교환이 가능하도록 만듭니다.  알고리즘을 사용하는 클라이언트와 상관없이 독립적으로 알고리즘을 다양하게 변경할 수 있습니다.

 

아주 간단히 말하자면 무엇을 할지에 따라 사용할 알고리즘Strategy을 갈아끼우는 방식으로 이해할 수 있습니다. 전략 패턴은 특정 알고리즘에 종속되어 동작하지 않으며, 언제든지 알고리즘을 변경해서 적용할 수 있습니다. 실제 내부 동작을 외부 알고리즘 객체Strategy로 분리하여 유연하게 동작을 변경시킬 수 있습니다.

 

참고로, 전략 패턴은 정책policy 패턴이라고도 부릅니다.

 

 

전략 & 전술

전략은 어떤 목표를 정하고 진행하는 큰 틀을 말하며, 즉 앞으로 무엇을 할지 계획하는 것으로 ‘What to’를 의미합니다. 전술은 전략을 짜면서 정한 목표를 달성하기 위한 상세 내용이며 ‘How To’를 의미합니다.

 

전술은 전략을 수행하기 위한 효과적인 행동을 말하는데,

전략 패턴에서 전략알고리즘이고 전술실제 알고리즘이 동작하는 상세 내용입니다.

 

 

Strategy Pattern 특징

전략 패턴은 알고리즘의 객체를 교환하여 사용한다는 측면에서 유용한 패턴입니다.

교환되는 처리 로직을 알고리즘화하여 객체의 군을 형성할 수도 있습니다.

 

알고리즘 캡슐화를 통해 조건문 없이도 원하는 행동으로 교체할 수 있습니다.

하지만 알고리즘 객체가 교체된다는 점에서 실행 시 많은 수의 객체를 갖는다는 단점도 있습니다.

 

 

 

왜 필요할까?

전략 패턴을 사용하는 이유는 아래와 같이 정리할 수 있습니다.

 

 

Strategy의 필요성

✔️  변화

문제를 해결하기 위한 해결 방법이 변화되기 때문입니다.

 

한 번 개발된 프로그램은 산업 현장에서 생각보다 오랫동안 사용되는데요.

이러한 프로그램은 시간이 지나면서 환경적인 영향을 받으므로 변화가 필요합니다.

환경적인 영향이라고 하면 개발 트렌드나 보안성 등의 다양한 이유가 있습니다.

 

프로그램의 버그 또한 환경 변화에 의해 발생되며 이 경우 코드 수정이 필요합니다.

간단한 변화는 코드 몇 줄만 수정해도 해결할 수 있지만, 잦은 코드 수정으로 변경 사항이 누적되면 더 이상 수정이 어려워집니다. 또, 큰 환경 변화가 있을 때는 코드 수정이 어려운데, 이 때에는 기존의 방법을 버리고 새로운 방법을 도입하는 것이 현명할 수 있습니다. 이때 리팩터링이 필요하고, 새로운 알고리즘으로 교체하는 경우가 발생합니다.

 

 

✔️ 다양성

문제를 해결하기 위한 해결 방법이 다양하기 때문입니다.

 

프로그램의 목표는 주어진 문제를 해결하는 것입니다. 알고리즘은 바로 이 문제를 해결하는 하나의 패턴이죠. 문제를 해결하는 방법은 다양하며 한 가지 문제를 해결하는 방법은 수십, 수백 가지가 존재하며, 다르지만 유사한 방법으로 해결할 수도 있습니다.

 

문제를 해결하는 방법을 다르게 적용하려고 할 때 어떻게 해야할까요?

해결 방법이 변경되면 관련 코드도 같이 수정해야 하고, 이때 코드를 찾고 오래된 코드를 다시 분석하는 등 많은 시간이 필요하죠. 해결 방법을 따로 분리해두고 필요한 것을 갈아끼우면 어떨까요?

 

예를 들어 정렬 알고리즘을 사용한다고 했을 때, 같은 문제 해결하고자 할 때 어떤 정렬 알고리즘을 이용해도 똑같이 해결할 수 있죠. 하지만, 데이터의 양이나 성질에 따라서 유리한 알고리즘이 다릅니다.  정렬을 위해 알고리즘을 변경하고자 할 때 사용하고자하는 전략(알고리즘)을 바꾸기만 하면 손쉽게 적용할 수 있습니다.

 

이제 전략 패턴의 유용성이 잘 받아들여지시나요?

 

✔️ 분리

코드를 분리해서 관리하면 유지 보수 측면에서 유리하기 때문입니다.

프로그램이 외부 변화에 보다 쉽게 적응하려면 변화가 예상되는 부분을 분리하는 것이 좋습니다.

 

 

 

 

 

Strategy의 구조

 

 

 

 

 

전략 패턴은 Context, 추상 객체인 Strategy와 그의 구현부들로 구성됩니다.

위에서 말해온 "알고리즘"이 바로 "Strategy를 구현한 객체"들에 해당되겠죠.

 

알고리즘은 복잡한 문제를 어떤 방법으로 실행했을 때 문제가 해결되는 방법입니다.

알고리즘Strategy는 구조를 유지하기 위해서 인터페이스interface class가 필요합니다.

 

또, 알고리즘은 문제 해결을 위한 동작을 다양하게 갖고 있는데, 이 동작은 추상화abstract method하여 처리할 수 있습니다.

포스팅을 하면서 다양한 코드들을 찾아보았는데, 메소드 추상화를 적용하는 곳도 있고 적용하지 않은 곳도 있어서 '합니다'보다는 '할 수 있는'으로 적었습니다 ㅎㅎ

사용자는 알고리즘의 내부 실행 동작을 이해할 필요가 없으며,

알고리즘을 사용할 수 있는 인터페이스만 확인하고 절차에 따라 호출해서 사용하면 됩니다.

 

 

Sample Code: Java

게임을 한다고 생각해보세요.

어떤 공격을 하고 싶을 때, 다른 무기로 교체해야합니다.

'공격' 이라는 행위는 동일하지만, 어떤 무기로 공격할지를 결정해야합니다.

이때, 어떤 무기로 공격할지가 바로 'What To' - 전략을 선택하는 것입니다.

전략을 선택했으면, 어떻게 공격할지를 구현해야 합니다. 이것을 'How To' 인 전술로 파악할 수 있겠죠.

 

이제 코드를 통해 확인해보겠습니다.

코드는 interface와 abstract를 함께 사용한 예시입니다.

 

 

Strategy

public interface Weapon {
    void attack();
}
public abstract class Strategy {
    protected Weapon delegate;

    public void setWeapon(Weapon weapon) {
        System.out.println("-- 무기 교환 --");
        this.delegate = weapon;
    }

    public abstract void attack();
}

 

 

ConcreteStrategy

public class Knife implements Weapon {
    @Override
    public void attack() {
        System.out.println("칼 공격");
    }
}
public class Gun implements Weapon {
    @Override
    public void attack() {
        System.out.println("총 발포");
    }
}

 

 

Context

public class Fighter extends Strategy {
    @Override
    public void attack() {
        if (this.delegate == null) {
            // 무기 선택 안한 경우
            System.out.println("맨손 공격");
        } else {
            this.delegate.attack();
        }
    }
}

 

 

Main

public class Main {
    public static void main(String[] args) {
        Fighter obj = new Fighter();
        obj.attack();

        obj.setWeapon(new Knife());
        obj.attack();

        obj.setWeapon(new Gun());
        obj.attack();
    }
}

 

출력 결과

맨손 공격
-- 무기 교환 --
칼 공격
-- 무기 교환 --
총 발포

 

동적 교체

전략패턴을 사용하는 이유는 동적으로 알고리즘을 교체하기 위함입니다.

전략을 위한 알고리즘은 다양하며 알고리즘을 교체하거나 결합하려면 일정한 규격이 필요합니다.

전략 패턴을 공통적으로 적용하려면 인터페이스를 설계하고, 인터페이스에 의해 알고리즘을 캡슐화합니다.

 

인터페이스로 정의된 기능들을 호출하여 위의 Knife와 Gun와 같이 동적으로 교체하며 복수의 기능을 호출해 처리할 수도 있습니다. 위임되는 객체의 정보는 setter의 매개변수로 전달합니다. 복합 관계를 가진 내부 프로퍼티를 참조하여 객체를 서로 교환하고, 위임을 통해 내부 처리를 변화시킬 수 있습니다.

 

 

 

 

관련 패턴

C- Creational Patterns  |  S - Structural Patterns  |  B - Behavioral Patterns

 

 

C: Abstract Factory

전략 패턴은 알고리즘을 교체하여 사용합니다.

추상 팩토리는 공장, 부품, 제품 등 객체를 교체할 수 있는데, 이렇게 객체를 교체한다는 측면에서 전략 패턴과 유사점이 있습니다.

다만 추상 팩토리는 객체의 생성을 책임지고 전략 패턴은 사용되는 알고리즘, 즉 행동을 관리한다는 것이 다릅니다.

 

 

S: Flyweight

전략 패턴으로 분리된 알고리즘 객체를 공유하여 사용하는 경우도 있습니다.

알고리즘을 공유할 때 플라이웨이트 패턴을 응용할 수 있습니다.

 

 

B: State

많은 분들이 Strategy Pattern VS State Pattern 를 많이 검색해볼만큼 둘은 꽤나 비슷합니다.

 

전략 패턴은 알고리즘을 위임으로 사용합니다. 위임을 적극적으로 적용하여 관계를 형성하는 것이 상태 패턴과 많이 유사합니다.

행동 패턴에 유사한 점은 있지만 처리하려는 목적이 서로 다릅니다.

전략 패턴의 경우 변경여부는 정할 수 있지만 필요에 의해 알고리즘을 변경하는데, 상태 패턴에서는 상태가 변할 때 위임 객체도 변경됩니다.

 

Strategy 패턴은 지정된 특정 메소드가 모듈화된 상태에 따라 다르게 실행되도록 하는 것이라면, 

State 패턴은 그 메소드가 실행될 때마다 상태도 전환되도록 하는 것이라고 이해할 수 있습니다.

 

예를 들어 A와 B, C라는 상태가 있을 때 

Strategy 패턴은 상태 A, B, C에 해당하는 알고리즘이 실행되고

State 패턴은 메소드에 따라 A와 B, C라는 상태가 변경되는 것입니다.

 

 

 

 

그럼 지금까지 Strategy Pattern에 대해 알아보았습니다.

오타나 잘못된 내용이 있다면 댓글로 남겨주세요!

감사합니다 ☺️ 

 

 

모든 Design Patterns 모아보기

 

Design Patterns

안녕하세요. GoF 디자인 패턴을 정리하고자 합니다. 디자인 패턴은 일주일 전부터 공부를 시작했는데, 스스로 설명하듯 적는게 익히는데 도움이 클 것같아 정말 오랜만에 시리즈로 포스팅하려

gngsn.tistory.com

 

 

 

'ETC > Design Patterns' 카테고리의 다른 글

Design Pattern, Factory Method  (0) 2022.01.03
Design Pattern, Template Method  (0) 2022.01.01
Design Pattern, Builder  (1) 2021.12.29
Design Pattern, Facade  (0) 2021.12.26
Design Patterns, 제대로 이해하기  (1) 2021.12.26