Strategy (전략패턴)

2022. 3. 24. 00:00CSharp/Design Pattern

반응형

일련의 알고리즘을 정의하고 각 알고리즘을 별도의 클래스에 넣고

해당 개체를 교환할 수 있도록 하는 행동 디자인 패턴이다.

실행 중에 알고리즘을 선택할 수 있다.

  • 특정한 계열의 알고리즘들을 정의하고
  • 각 알고리즘을 캡슐화하며
  • 이 알고리즘들을 해당 계열 안에서 상호 교체가 가능하게 만든다.

 

문제점

내비게이션 앱을 만들기로 했다. 앱에는 자동 경로 계획 기능이 있다.

사용자는 주소를 입력하고 지도에 표시된 해당 목적지로 가는 가장 빠른 경로를 볼 수 있어야 한다.

앱의 첫 번째 버전은 도로 위의 경로만 만들 었다.

앱의 두 번째 버전은 도보 경로를 만드는 옵션을 추가했다. 

그 직후 사람들이 경로에서 대중 교통을 사용할 수 있도록 다른 옵션을 추가했다.

나중에 자전거 타는 사람을 위한 경로 구축을 추가할 계획이고

도시의 모든 관광 명소를 통과하는 경로를 구축하는 또 다른 옵션이다.

또....

그리고...

 

새 라우팅 알고리즘을 추가할 때마다 내비게이터의 기본 클래스 크기가 엄청나게 늘어난다. 

버그 수정이든 거리 점수의 약간의 조정이든 알고리즘 중 하나를 변경하면 전체 클래스에 영향을 미친다.

#$#%$^??????

 

 

해결책

다양한 방식으로 특정 작업을 수행하는 클래스를 선택하고

이러한 모든 알고리즘을 전략 이라고 하는 별도의 클래스로 추출하자. 

 

 

 

 

class diagram

 

 

Context 

:  구체적인 전략 중 하나에 대한 참조를 유지하고 전략 인터페이스를 통해서만 이 객체와 통신

 

Strategy (interface)

:  컨텍스트가 전략을 실행하는 데 사용하는 방법을 선언. ConcreateStrategies 의 공통 인터페이스

 

ConcreateStrategies

: 실제 Strategy 를 구현한 class

 

적용

  • 객체 내에서 다양한 알고리즘 변형을 사용하고 런타임 중에 알고리즘을 변경 하려는 경우
  • 일부 동작을 실행하는 방식만 다른 유사한 클래스가 많이 있는 경우 
  • 컨텍스트에서  클래스의 알고리즘을 분리 하려는 경우
  • 클래스에 동일한 알고리즘의 다른 많은 변형이 있어 조건식 (if...else)이 복잡하게 얽혀있는 경우

 

MMORPG 에서 각 직업의 공격와 방어에 대한 내용을 전략 패턴을 이용해 구현해 보자

Strategy 구현

public interface IStrategy
{
    void Action();
}
public interface IAttack : IStrategy { }
public interface IDefence : IStrategy { }

public abstract class Attack : IAttack
{
    public abstract void Action();
}

public class Sword : Attack
{
    public override void Action()
    {
        Console.WriteLine("검으로 벤다.");
    }
}

public class Dagger : Attack
{
    public override void Action()
    {
        Console.WriteLine("단검으로 찌른다.");
    }
}
public class Punch : Attack
{
    public override void Action()
    {
        Console.WriteLine("주먹을 휘두른다.");
    }
}

public abstract class Defence : IDefence
{
    public abstract void Action();
}

public class Shield : Defence
{
    public override void Action()
    {
        Console.WriteLine("방패로 막는다.");
    }
}

public class Evasion : Defence
{
    public override void Action()
    {
        Console.WriteLine("공격을 회피한다.");
    }
}

Context 구현

public interface IUnit
{
    void Attack();
    void Defence();
    void SetAttack(IAttack attack);
    void SetDefence(IDefence defence);
}

public abstract class Unit : IUnit
{
    protected IStrategy _attack;
    protected IStrategy _defence;
    public void Attack()
    {
        Console.Write($"{this.GetType().Name} (이)가 ");
        _attack.Action();
    }
    public void Defence()
    {
        Console.Write($"{this.GetType().Name} (이)가 ");
        _defence.Action();
    }
    public void SetAttack(IAttack attack) => _attack = attack;
    public void SetDefence(IDefence defence) => _defence = defence;
}

public class Warrior : Unit { }
public class Rogue : Unit { }
public class Assassin : Unit { }

사용법

IUnit u1 = new Warrior();
u1.SetAttack(new Sword());
u1.Attack();
u1.SetDefence(new Shield());
u1.Defence();

IUnit u2 = new Rogue();
u2.SetAttack(new Dagger());
u2.Attack();
u2.SetDefence(new Evasion());
u2.Defence();

IUnit u3 = new Assassin();
u3.SetAttack(new Sword());
u3.Attack();
u3.SetDefence(new Evasion());
u3.Defence();
//output
Warrior (이)가 검으로 벤다.
Warrior (이)가 방패로 막는다.
Rogue (이)가 단검으로 찌른다.
Rogue (이)가 공격을 회피한다.
Assassin (이)가 검으로 벤다.
Assassin (이)가 공격을 회피한다.

 

관련영상

https://youtu.be/vKRKfBsj6KQ

 

 

반응형

'CSharp > Design Pattern' 카테고리의 다른 글

Visitor (방문자)  (0) 2022.03.25
Observer  (0) 2022.03.23
Mediator  (0) 2022.03.22
Command  (0) 2022.03.21
Template Method  (0) 2022.03.18