Command Pattern
# Introduce
오늘 소개드릴 디자인 패턴은 커맨드 패턴입니다.
커맨드 패턴은 실행될 기능을 캡슐화하여 재사용에 용이한 클래스를 구현하는 것을 말합니다.
어떤 객체(A)에서 다른 객체(B)의 메서드를 실행하려면 그 객체(B)를 참조하고 있어야 하는 의존성이 발생합니다.
그러나 커맨드 패턴을 적용하면 의존성을 제거할 수 있습니다.
# Pros & Cons
장점
1. 작업을 요청하는 클래스(Invoker)와 실제로 작업을 수행하는 클래스(Receiver) 사이의 의존성이 사라집니다.
2. 기존 코드를 수정하지 않고 새로운 커맨드 및 기능을 쉽게 추가할 수 있습니다.
3. 커맨드 패턴을 통해 확장성이 높아질 수 있습니다.
단점
1. 모든 작업이 독립적인 ConcreteCommand 클래스이므로 구현 및 유지보수해야 하는 클래스가 많습니다.
2. 단 하나의 커맨드에 대해 클래스가 많아집니다.
# For example
- 무기를 작동시키는 커맨드 패턴 예시
1. 무기 클래스 (Receiver) 작성
//Receiver
public class Weapon
{
//모든 무기에 공통적으로 들어가는 변수
protected float damage;
protected float coolTime;
}
public class Knife : Weapon
{
//찌르기
public void Stab()
{
Debug.Log("Stab");
}
}
public class Gun : Weapon
{
//총에 공통적으로 들어가는 변수
protected int maxBulletCount;
protected int nowBulletCount;
protected float reloadCoolTime;
protected float maxRange;
//발사
public virtual void Shoot()
{
Debug.Log("Shoot");
}
//재장전
public virtual void Reload()
{
Debug.Log("Reload");
}
}
public class Ak47 : Gun
{
public override void Shoot()
{
Debug.Log("Ak47 Shoot");
}
public override void Reload()
{
Debug.Log("Ak47 Reload");
}
}
2. 커맨드 클래스 작성
public interface IWeaponCommand
{
void Execute();
}
public class ReloadGunCommand : IWeaponCommand
{
private Gun gun;
public void Execute()
{
//재장
gun.Reload();
}
public ReloadGunCommand(Gun val)
{
gun = val;
}
}
public class ShootGunCommand : IWeaponCommand
{
private Gun gun;
public void Execute()
{
//총쏘기
gun.Shoot();
}
public ShootGunCommand(Gun val)
{
gun = val;
}
}
public class StabKnifeCommand : IWeaponCommand
{
private Knife knife;
public void Execute()
{
//찌르기 실행
knife.Stab();
}
public StabKnifeCommand(Knife val)
{
knife = val;
}
}
3. 커맨드를 호출하는 클래스 (Invoker) 작성
//Invoker
using System.Collections.Generic;
public class CommandManager
{
private Dictionary<string, IWeaponCommand> commandDic = new Dictionary<string, IWeaponCommand>();
//커맨드를 세팅
public void SetCommand(string name, IWeaponCommand command)
{
if (commandDic.ContainsValue(command))
{
Debug.Log("이미 커맨드가 포함되어있음.");
return;
}
commandDic.Add(name, command);
}
//저장된 특정 커맨드를 실행
public void InvokeExecute(string name)
{
commandDic[name].Execute();
}
}
4. 커맨드 사용 클래스 작성
public class WeaponSystem : MonoBehaviour
{
CommandManager commandMgr = null;
void Start()
{
//Invoker
commandMgr = new CommandManager();
//Receiver
M4A1 m4a1 = new M4A1();
Knife knife = new Knife();
//커맨드를 생성하고 연결
ShootGunCommand shootM4a1Command = new ShootGunCommand(m4a1);
ReloadGunCommand reloadM4a1Command = new ReloadGunCommand(m4a1);
StabKnifeCommand stabKnifeCommand = new StabKnifeCommand(knife);
//커맨드매니저에 커맨드를 세팅
commandMgr.SetCommand("leftClick", shootM4a1Command);
commandMgr.SetCommand("RKey", reloadM4a1Command);
commandMgr.SetCommand("FKey", stabKnifeCommand);
}
void Update()
{
if(Input.GetMouseButton(0))
{
commandMgr.InvokeExecute("leftClick");
}
if(Input.GetKeyDown(KeyCode.R))
{
commandMgr.InvokeExecute("RKey");
}
if (Input.GetKeyDown(KeyCode.F))
{
commandMgr.InvokeExecute("FKey");
}
}
}
# Epilogue
커맨드 패턴을 잘 활용하면 커맨드 활용에 대하여 유지 보수 및 활용도가 올라갑니다.
커맨드 패턴을 통하여 리플레이, undo 등 다양한 활용을 해보시길 바랍니다.
감사합니다.
# Reference
korstrix.github.io/unity/unitylibrary/designpattern/github/Command_Pattern/
'개발 > Design Pattern' 카테고리의 다른 글
SendMessage로 FSM 구현해보기 (Unity3D) (0) | 2022.01.27 |
---|---|
Singleton (0) | 2022.01.14 |