Command

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

반응형

요청을 객체의 형태로 캡슐화하여 사용자가 보낸 요청을 나중에 이용할 수 있도록 매서드 이름, 매개변수 등 요청에 필요한 정보를 저장 또는 로깅, 취소할 수 있게 하는 패턴이다

 

 

문제점

Button 을 만들었다고 가정해 보자

이 Button 의 종류가 여러가지가 있고 각각이 비슷해 보이지만 다른 기능을 수행 한다고 하자.가장 간단한 해결책은 버튼별로 하위 클래스 를 만드는 것이다.이러한 하위 클래스는 버튼 클릭시 실행되어야 하는 코드가 포함된다. 

https://refactoring.guru/design-patterns/command

 

이런 형태가 되게 되면 기본 class ( Button) 의 코드가 변경되었을때 하위 클래스의 코드가 영향을 받게될 가능 성이 있다.

 

일부 작업들 (복사 / 붙혀넣기) 은 여러 위치에서 호출될 수 있다.

 

복사 도구모음복사 버튼복사 context menuCtrl+C 키보드

 

이렇게 코드를 복제하거나 버튼에 종속된 메뉴를 만들어야 하는 경우가 생긴다. 

이는 코드에 복잡성을 증가시켜 유지 보수가 힘든 코드의 원인이 된다. 

 

해결책

호출되는 개체, 메서드 이름 및 인수 목록과 같은 모든 요청 정보를 

이 요청을 트리거 하는 단일 메서드가 있는 별도의 command class 로 추출해야 한다. 

https://refactoring.guru/design-patterns/command

 

 

다음 단계는 이 command (명령) 가 동일한 인터페이스를 구현 하도록 하는 것

이 인터페이스를 사용하면 구체적인 명령 클래스에 연결하지 않고도

동일한 요청 발신자와 함께 다양한 명령을 사용할 수 있다. 

https://refactoring.guru/design-patterns/command

 

 

 

class diagram

https://refactoring.guru/design-patterns/command

 

 

 

 

적용

  • 작업으로 개체를 매개변수화하려는 경우
  • 작업을 대기열에 넣거나 실행을 예약하거나 원격으로 실행하려는 경우
  • 되돌릴 수 있는 작업을 구현하려는 경우

 

Invoker(sender) 

: 요청의 시작, 명령 개체에 대한 참조를 저장,

요청을 수신자에게 직접 보내는 대신 해당 명령을 트리거

일반적으로 생성자를 통해 클라이언트에서 미리 생성된 명령을 받는다.

 

Command(interface)

: 일반적으로 명령을 실행하기 위한 단일 메서드만 선언 (execute)

 

ConcreateCommand

: 다양한 종류의 요청을 구현 구체적인 명령은 자체적으로 작업을 수행하는 것이 아니라 비즈니스 논리 개체 중 하나로 호출을 전달해야 한다. 그러나 코드를 단순화하기 위해 이러한 클래스를 병합할 수 있다.

수신 개체에서 메서드를 실행하는 데 필요한 매개 변수는 구체적인 명령의 필드로 선언할 수 있다. 생성자를 통해서만 이러한 필드의 초기화를 허용하여 명령 개체를 변경할 수 없도록 만든다.

 

Receiver

:클래스에는 몇 가지 비즈니스 로직이 포함되어 있다. 
거의 모든 개체가 수신기 역할을 할 수 있다. 
실제 작업을 수행

 

 

공격과 방어를 command 로 처리 해 보자

 

Command 및 Receiver 구현

public interface ICommand
{
    void Execute();
}

public class AttackCommand : ICommand
{
    private readonly AttackCommandReceiver _receiver;
    private readonly int _attackPower;
    private readonly int _attackCount;

    public AttackCommand(AttackCommandReceiver receiver, int attackPower, int attackCount)
    {
        _receiver = receiver;
        _attackPower = attackPower;
        _attackCount = attackCount;
    }
    public void Execute()
    {
        _receiver.Handle(_attackPower, _attackCount);   
    }
}

public class AttackCommandReceiver
{
    public void Handle(int attackPower, int attackCount)
    {
        Console.WriteLine($"{attackPower} 공격력으로 {attackCount} 번 공격");
    }
}

public class DefenceCommand : ICommand
{
    private readonly DefenceCommandReceiver _receiver;
    private readonly double _defenceRate;

	public DefenceCommand(DefenceCommandReceiver receiver, double defenceRate)
    {
        _receiver = receiver;
        _defenceRate = defenceRate;
    }
    public void Execute()
    {
        _receiver.Handle(_defenceRate);
    }
}

public class DefenceCommandReceiver
{
    public void Handle(double defenceRate)
    {
        Console.WriteLine($"{defenceRate * 100} % 확률로 방어");
    }
}

Invoker 구현

public class UnitInvoker
{
    private ICommand _command;
    public void SetCommand(ICommand command)
    {
        _command = command;
    }

    public void MoveAfterAction()
    {
        Console.WriteLine("앞으로 한칸 이동 후 ");
        _command.Execute();
    }

}

사용법

UnitInvoker invoker = new UnitInvoker();
invoker.SetCommand(new AttackCommand(new AttackCommandReceiver(), 10, 5));
invoker.MoveAfterAction();
invoker.SetCommand(new DefenceCommand(new DefenceCommandReceiver(), 0.5));
invoker.MoveAfterAction();
//output
앞으로 한칸 이동 후
10 공격력으로 5 번 공격
앞으로 한칸 이동 후
50 % 확률로 방어

 

 

 

관련영상

https://youtu.be/yBV4-syqStw

 

 

 

반응형

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

Observer  (0) 2022.03.23
Mediator  (0) 2022.03.22
Template Method  (0) 2022.03.18
State  (0) 2022.03.17
Memento  (0) 2022.03.16