[C#] 커맨드 패턴

참조


커맨드 패턴이란

  • 커맨드 패턴이란, 실행될 기능을 캡슐화함 으로써 주어진 여러 기능을 실행할 수 있는 재사용성이 높은 클래스를 설계하는 패턴을 의미합니다.
  • 즉, 어떤 이벤트가 발생했을 때 실행 될 기능이 다양하면서도 변경이 필요한 경우에 이벤트를 발생시키는 클래스를 변경하지 않고 재사용할 때 유용하게 사용할 수 있는 디자인 패턴입니다.

커맨드 패턴 다이어그램

  • 다음은 커맨드 패턴을 보다 쉽게 이해하기 위한 다이어그램입니다.

  • 실행될 기능을 캡슐화함으로써 기능의 실행을 요구하는 호출자(Invoker) 클래스와 실제 기능을 실행하는 수신자(Receiver) 클래스 사이의 의존성을 제거합니다.

  • 따라서, 실행될 기능의 변경에도 호출자 클래스를 수정 없이 그대로 사용 할 수 있도록 합니다.

    • Command
      • 실행될 기능에 대한 인터페이스
      • 실행될 기능을 Execute 메서드로 선언함
    • ConcreateCommand
      • 실제로 실행되는 기능을 구현
      • 즉, Command 라는 인터페이스를 구현
    • Invoker
      • 기능의 실행을 요청하는 호출자 클래스
    • Receiver
      • ConcreateCommand 에서 Execute 메서드를 구현할 때 필요한 클래스
      • 즉, ConcreateCommand 의 기능을 실행하기 위해 사용하는 수신자 클래스
  • 위 개념을 토대로, 커맨드 패턴 예제 코드를 작성해 보도록 하겠습니다.


예제코드

  • 커맨드 패턴을 Invoker 클래스, Command 클래스/인터페이스, ConcreateCommand 클래스, Receiver 클래스로 구성됩니다.
  • 커맨드 패턴을 이용하여 제품의 가격을 수정하는 프로그램을 작성해 보겠습니다.
  • 먼저, Product 기본 비즈니스를 포함해야 하는 Receiver 클래스부터 작성하겠습니다.

Product

namespace CommandPattern
{
    internal class Product
    {
        public string Name { get; set; }
        public int Price { get; set; }
        public Product(string name, int price)
        {
            Name = name;
            Price = price;
        }
        public void IncreasePrice(int amount)
        {
            Price += amount;
            Console.WriteLine($"The price for the {Name} has been increased by {amount}$.");
        }
        public void DecreasePrice(int amount)
        {
            if (amount < Price)
            {
                Price -= amount;
                Console.WriteLine($"The price for the {Name} has been decreased by {amount}$.");
            }
        }
        public override string ToString() => $"Current price for the {Name} product is {Price}$.";
    }
}
  • 위 코드는 매우 간단한 비즈니스 논리를 가진 Receiver 클래스입니다.
  • 간단히 제품의 가격을 높이거나 낮추는 역할을 가지고 있습니다.
  • 이제 Client 클래스는 Product 클래스를 객체화 하고, 필요한 작업을 실행할 수 있습니다.
  • 그러나 커맨드 패턴을 Receiver 클래스를 직접 사용해서는 안됩니다.
  • 대신, 모든 요청 세부 정보를 Command 클래스로 추출해야 합니다.
  • 다음으로 ICommand 인터페이스를 추가합니다.

ICommand.cs

namespace CommandPattern
{
    public enum PriceAction
    {
        Increase,
        Decrease
    }

    internal interface ICommand
    {
        void ExecuteAction();
    }
}
  • 이제, 실제 Command 명령을 받았을때 Execute 하는 ProductCommand 클래스를 작성해 보도록 하겠습니다.

ProductCommand.cs

namespace CommandPattern
{
    internal class ProductCommand : ICommand
    {
        private readonly Product _product;
        private readonly PriceAction _priceAction;
        private readonly int _amount;
        public ProductCommand(Product product, PriceAction priceAction, int amount)
        {
            _product = product;
            _priceAction = priceAction;
            _amount = amount;
        }

        public void ExecuteAction()
        {
            if (_priceAction == PriceAction.Increase)
            {
                _product.IncreasePrice(_amount);
            }
            else
            {
                _product.DecreasePrice(_amount);
            }
        }
    }
}
  • 위 코드를 보게되면, ProductCommand 클래스에는 Command 요청에 대한 모든 정보가 구현되어 있습니다.
  • 다음으로, Invoker(호출자) 역할을 하는 ModifyPrice 클래스를 추가해 보겠습니다.

ModifyPrice.cs

namespace CommandPattern
{
    internal class ModifyPrice
    {
        private readonly List<ICommand> _commands;
        private ICommand _command;

        public ModifyPrice()
        {
            _commands = new List<ICommand>();
        }

        public void SetCommand(ICommand command) => _command = command;

        public void Invoke()
        {
            _commands.Add(_command);
            _command.ExecuteAction();
        }
    }
}
  • ModifyPrice 클래스는 ICommand 인터페이스를 구현하고 모든 작업을 저장하는 모든 명령과 함께 동작할 수 있습니다.
  • 마지막으로 Client 코드 작업을 진행하겠습니다.

Program.cs

using CommandPattern;

// Invoker
var modifyPrice = new ModifyPrice();
var product = new Product("Phone", 500);

Execute(product, modifyPrice, new ProductCommand(product, PriceAction.Increase, 100));
Execute(product, modifyPrice, new ProductCommand(product, PriceAction.Increase, 50));
Execute(product, modifyPrice, new ProductCommand(product, PriceAction.Decrease, 25));

Console.WriteLine(product);

void Execute(Product product, ModifyPrice modifyPrice, ICommand productCommand)
{
    modifyPrice.SetCommand(productCommand);
    modifyPrice.Invoke();
}

실행 결과

  • 실행 결과, Invoker 클래스인 ModifyPrice 클래스 객체를 선언하고, Execute 메서드에 Product 객체를 넘겨주어 ProductCommand 클래스에게 명령을 실행하여 최종적으로 수정된 가격이 반영되어 출력되는 것을 확인할 수 있습니다.
The price for the Phone has been increased by 100$.
The price for the Phone has been increased by 50$.
The price for the Phone has been decreased by 25$.
Current price for the Phone product is 625$.
728x90

이 글을 공유하기

댓글

Designed by JB FACTORY