디자인 패턴 (Design Pattern)

[행동 패턴] 전략 패턴(Strategy Pattern)

계란💕 2023. 2. 27. 12:51

인터페이스(Interface)

  •  키보드나 디스플레이 따위처럼 사람과 컴퓨터를 연결하는 장치
    • 기능에 대한 선언과 기능과 구현을 분리한다.
    • 기능을 사용하는 통로
    • 어떤 기능에 접근하는 접근점을 제공한다.

델리게이트(Delegate)

  • 위임하다. 떠넘긴다는 뜻
  • 두 객체간의 관계에서 어떤 객체가 기능을 수행할 때, 복잡한 기능을 개발하는 책임을 다른 객체로 떠넘긴다.

전략 패턴(스트레지티 패턴, Strategy Pattern)

  • Def) 여러 알고리즘을 하나의 추상적인 접근점(인터페이스)을 만들어서 접근 점에서 서로 교환 가능하도록 하는 패턴을 말한다. 
  • 의도: 알고리즘들의 패밀리를 정의하고 각 패밀리를 별도의 클래스에 넣은 다음 그들의 객체들을 상호교환하도록 하는 행동 디자인 패턴을 말한다. 
  • 해결책: 특정 작업을 다양한 방식으러 수행하는 클래스를 선택한 후, 모든 알고리즘을 Strategies라는 별도의 클래스들로 추출한다. 
  • 전략 패턴 적용 방법
    • 객체 내에서 한 알고리즘의 다양한 변형을 사용하고 싶을 때, 런타임 중에 한 알고리즘에서 다른 알고리즘으로 전환하고 싶을 때 사용한다. - LSP(리스코프 치환법칙)
    • 일부 행동을 실행하는 방식에서만 차이가 있고 다른 부분은 유사한 클래스에 대해 적용한다. 
    • 클래스의 비즈니스 로직을 해당 로직의 콘텍스트에서 그리 중요하지 않을 수 있는 알고리즘들의 구현 세부 사항들로부터 고립시킨다. - DIP(의존 관계 역전원칙)
    • 같은 알고리즘의 다른 변형들 사이를 전환하는 거대한 조건문이 클래스에 있는 경우에 사용한다. 

전략 패턴 구조

  • 클라이언트(client): 전략 소유자 (사용자)는 특정 전략 객체를 만들어서 콘텍스트에 전달한다. 
  • 컨텍스트(Context): 구상 전략 중 하나에 대한 참조를 유지하고 전략 인터페이스를 통해서만 이 객체와 통신한다. 
  • 전략(Strategy) 인터페이스는 작업을 자체적으로 실행하는 게 아니라 Strategy 객체에 위임한다. 구상 전략을 실행하는데 필요한 공통 메서드를 포함한다.  
  • 구상 전략들(Strategies): 컨텍스트가 사용하는 알고리즘의 다양한 변형들을 구현한다. 
  • 서로 다른 전략끼리 의존하지 않도록 한다.
    • 따라서 새로운 알고리즘을 추가하거나 기존 알고리즘을 수정할 수 있다. 

 

 

 

 


전략 패턴 장점

  • 런타임에 한 객체 내부에서 사용되는 알고리즘을 교환 가능
  • 알고리즘의 구현 세부 정보들을 고립시킬 수 있다. 
  • 상속을 합성으로 대체 가능
  • 개방 폐쇄 원칙 : 컨텍스트를 변경하지 않고도 새로운 전략 도입 가능

 

전략 패턴 단점

  • 알고리즘이 몇 개 없고 변하지 않는다면 새로운 클래스들과 인터페이스들로 프로그램을 지나치게 복잡하게 만들 필요 없다. 
  • 클라이언트들은 적절한 전략을 선택할 수 있도록 전략 간 차이점을 알아야 한다. 

전략 패턴 구현 방법

  • 콘텍스트 클래스에서 자주 변경되는 알고리즘을 식별한다. 
  • 알고리즘의 모든 변형에 공통인 전략 인터페이스를 선언한다. 
  • 모든 알고리즘을 각각 하나씩 클래스들로 추출한다. 이들은 모두 전략 인터페이스를 implement한다. 
  • 콘텍스트 클래스에서 전략 객체에 대한 참조를 저장하기 위한 필드를 추가한 다음 해당 필드의 값을 대체하기 위한 세터를 제공한다.
  • 클라이언트들은 콘텍스트를 적젏한 전략과 연관시켜야한다. 

다른 패턴과의 관계

 

커맨드 패턴과  전략 패턴

  • 공통점: 어떤 작업으로 객체를 매개변수화하는데 사용할 수 있다. 
  • 차이점
    • 전략 패턴: 일반적으로 같은 작업을 수행하는 다양한 방법을 설명하므로 단일 콘텍스트 클래스 내에서 이러한 알고리즘들을 교환 가능하다.
    • 커맨드 패턴: 모든 작업을 객체로 변환 가능하다. 작업의 매개변수들은 해당 객체의 필드들이 된다. 

 

 

템플릿 메서드와 전략 패턴

  • 템플릿 메서드: 상속을 기반으로 한다. 이 메서드는 자식 클래스들에서 알고리즘의 부분들을 확장해서 변경할 수 있도록 한다. 클래스 수준에서 정의하므로 static
  • 전략 패턴: 합성을 기반으로 한다. 객체 수준에서 작동하므로 런타임에 행동들을 전환할 수 있도록 한다. 

  Ex) 신작 게임에서 캐릭터, 무기(칼, 검) 구현하기

 

  • 무기를 접근점으로 만든다. 
    • 다음과 같이 두 개의 클래스를 만든다.
<hide/>
public interface Weapon {

    public void attack();
}

 

<hide/>
public class Knife implements Weapon {
    @Override
    public void attack() {
        System.out.println("칼 공격");
    }
}

 

<hide/>
public class Sword implements Weapon {
    @Override
    public void attack() {
        System.out.println("검 공격");
    }
}

 

 

  • 캐릭터
    • setWeapon()을 통해 교환 가능하도록 한다. 
    • 구조 중에서 클라이언트에 해당한다.  ???
<hide/>
public class GameCharacter {
    private Weapon weapon;

    // 교환 가능
    public void setWeapon(Weapon weapon){
        this.weapon = weapon;
    }

    // delegate
    public void attack(){
       if(weapon == null){
            System.out.println("맨손 공격");
            return;
        }
        weapon.attack();
    }
}

 

  • Main
<hide/>
public static void main(String[] args) {
    GameCharacter character = new GameCharacter();
    character.attack();
    character.setWeapon(new Knife());
    character.attack();
    character.setWeapon(new Sword());
    character.attack();
}

 

  Note) 실행 결과

 

 

 

 

  Ex) 유지 보수 요청 - 새로운 무기  도끼를 추가하라

 

<hide/>
public class Ax implements Weapon {
    @Override
    public void attack() {
        System.out.println("도끼 공격");
    }
}

 

  Note) 실행 결과

  • 무기를 추가할 때 효율적으로 추가 가능하다. 
  • 게임 캐릭터를 건드리지 않으면서 매인 클래스 변경도 없다. 

 


궁금한 점 및 기타

  • 전에 했던 편결이 프로젝트가 어댑터 패턴 vs 전략 패턴?

 

 

출처  

https://www.inflearn.com/course/lecture?courseSlug=%EC%9E%90%EB%B0%94-%EB%94%94%EC%9E%90%EC%9D%B8-%ED%8C%A8%ED%84%B4&unitId=3171 https://refactoring.guru/ko/design-patterns/strategy

 

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