Strategy[Design Pattern]

SnowCat·2023년 3월 31일
0

Design Pattern

목록 보기
21/23
post-thumbnail

의도

전략 패턴 => 알고리즘의 패밀리를 정의해 각각을 클래스에 넣고, 객체간 상호작용을 할 수 있도록 하는 행동 디자인 패턴

문제

  • 지도앱을 만들고 있다 해보자. 주된 기능은 목적지로 가는 가장 빠른 경로를 나타내주는 것이다.
  • 처음에는 차량 이동만을 고려했지만, 기능을 추가하면서 도보, 대중교통, 자전거 등의 기능이 추가되었다.
  • 기능이 하나의 클래스에 있다면 클래스가 매우 거대해지고, 알고리즘을 수정할때마다 다른 컴포넌트에 영향을 주게 되는 치명적 문제점이 생기게 된다.

해결책

  • 특정 작업을 다양한 방식으로 수행하는 클래스를 선택해 모든 알고리즘을 전략(Strategy) 라는 별도의 클래스로 추출
  • 컨텍스트에는 여러 전략중 하나에 대한 참조를 저장하고, 작업 수행은 연결된 전략 객체에 위임함
  • 컨텍스트는 작업에 적합한 알고리즘에 대해서는 알지 못하며, 클라이언트가 원하는 전략을 받아 작업을 위임하기만 함

구조

/**
 * 클라이언트에 필요한 인터페이스를 정의하는 컨텍스트
 */
class Context {
    /**
     * 컨텍스트에는 하나의 전략 객체만이 저장되며, 자세한 내용을 컨텍스트가 알지는 못함
     * 컨텍스트는 모든 전략 객체와 소통할 수 있어야 함
     */
    private strategy: Strategy;

    /**
     * setter로도 구현 가능
     */
    constructor(strategy: Strategy) {
        this.strategy = strategy;
    }

    public setStrategy(strategy: Strategy) {
        this.strategy = strategy;
    }

    /**
     * 컨텍스트는 전략 객체에 실질적인 작업을 위임함
     */
    public doSomeBusinessLogic(): void {
        console.log('Context: Sorting data using the strategy (not sure how it\'ll do it)');
        const result = this.strategy.doAlgorithm(['a', 'b', 'c', 'd', 'e']);
        console.log(result.join(','));
    }
}

/**
 * 모든 객체에 적용되는 인터페이스 정의
 * 컨텍스트는 구상 전략 객체에 있는 알고리즘을 사용하게 됨
 */
interface Strategy {
    doAlgorithm(data: string[]): string[];
}

/**
 * 실제 전략 패턴 구현을 하는 부분
 * 이를 통해 컨텍스트가 런타임에서 컨텍스트를 바꿀 수 있게 됨
 */
class ConcreteStrategyA implements Strategy {
    public doAlgorithm(data: string[]): string[] {
        return data.sort();
    }
}

class ConcreteStrategyB implements Strategy {
    public doAlgorithm(data: string[]): string[] {
        return data.reverse();
    }
}

/**
 * 클라이언트 코드
 */
const context = new Context(new ConcreteStrategyA());
console.log('Client: Strategy is set to normal sorting.');
context.doSomeBusinessLogic();
/*
Context: Sorting data using the strategy (not sure how it'll do it)
a,b,c,d,e
*/

console.log('');

console.log('Client: Strategy is set to reverse sorting.');
context.setStrategy(new ConcreteStrategyB());
context.doSomeBusinessLogic();
/*
Context: Sorting data using the strategy (not sure how it'll do it)
e,d,c,b,a
*/

적용

  • 객체 내에서 한 알고리즘의 다양한 변형을 사용하고 싶을 때 사용
    런타임에서 알고리즘을 변형할 수 있으며, 객체의 행동을 다양하게 수행 가능하게 해주는 하위 객체와 연관시키는 것도 가능함
  • 알고리즘의 일부에서만 차이가 있는 유사한 클래스들이 많은 경우에 사용
    다양한 행동들을 별도의 클래스 계층구조로 추출하고 원래 클래스들을 하나로 결합해 중복 코드를 줄이게 해줌
  • 클래스의 비지니스 로직을 알고리즘의 구현 세부 사항과 분리시키고자 할 때 사용
    전략 패턴을 사용하면 코드의 나머지 부분에서 내부 데이터, 알고리즘의 의존 관계를 고립시킬 수 있음
  • 알고리즘의 분기를 담당하는 조건문이 매우 큰 상황에서 사용
    모든 알고리즘을 같은 인터페이스를 구현하는 별도의 클래스로 추출해 조건문을 제거할 수 있음

구현 방법

  1. 컨텍스트 클래스에서 자주 변경되는 알고리즘을 식별하기
  2. 알고리즘의 모든 변형에 공통으로 사용되는 전략 인터페이스 선언
  3. 모든 알고리즘을 자체 클래스로 추출하고 전략 인터페이스를 구현하기
  4. 컨텍스트 클래스에 전략 객체에 대한 참조를 저장하기 위한 필드를 추가하고, setter를 추가
    컨텍스트는 전략 인터페이스를 통해서만 전략 객체와 상호작용해야 함
  5. 컨텍스트의 클라이언트는 컨텍스트를 적절한 전략 객체와 연결시켜야 함

장단점

  • 런타임에 한 객체 내부에서 사용되는 알고리즘을 교환할 수 있음
  • 알고리즘을 사용하는 코드에서 알고리즘의 구현 세부 정보들을 고립시킬 수 있음
  • 상속을 합성으로 대체할 수 있음
  • 컨텍스트 변경 없이 새로운 전략 객체를 도입할 수 있어 개방, 폐쇄 원칙 준수
  • 알고리즘의 갯수가 적고 변동성이 적으면 굳이 전략 패턴을 사용할 필요가 없음
  • 클라이언트에서 적절한 전략을 사용하기 위해 전략 객체간 차이점을 알아야 함

출처:
https://refactoring.guru/ko/design-patterns/strategy

profile
냐아아아아아아아아앙

0개의 댓글