2022. 3. 24. 00:00ㆍCSharp/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 (이)가 공격을 회피한다.
관련영상
'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 |