[C#] 커맨드 패턴
- C#
- 2023. 3. 1. 20:39
참조
커맨드 패턴이란
- 커맨드 패턴이란, 실행될 기능을 캡슐화함 으로써 주어진 여러 기능을 실행할 수 있는 재사용성이 높은 클래스를 설계하는 패턴을 의미합니다.
- 즉, 어떤 이벤트가 발생했을 때 실행 될 기능이 다양하면서도 변경이 필요한 경우에 이벤트를 발생시키는 클래스를 변경하지 않고 재사용할 때 유용하게 사용할 수 있는 디자인 패턴입니다.
커맨드 패턴 다이어그램
- 다음은 커맨드 패턴을 보다 쉽게 이해하기 위한 다이어그램입니다.
실행될 기능을 캡슐화함으로써 기능의 실행을 요구하는 호출자(Invoker) 클래스와 실제 기능을 실행하는 수신자(Receiver) 클래스 사이의 의존성을 제거합니다.
따라서, 실행될 기능의 변경에도 호출자 클래스를 수정 없이 그대로 사용 할 수 있도록 합니다.
- Command
- 실행될 기능에 대한 인터페이스
- 실행될 기능을 Execute 메서드로 선언함
- ConcreateCommand
- 실제로 실행되는 기능을 구현
- 즉, Command 라는 인터페이스를 구현
- Invoker
- 기능의 실행을 요청하는 호출자 클래스
- Receiver
- ConcreateCommand 에서 Execute 메서드를 구현할 때 필요한 클래스
- 즉, ConcreateCommand 의 기능을 실행하기 위해 사용하는 수신자 클래스
- Command
위 개념을 토대로, 커맨드 패턴 예제 코드를 작성해 보도록 하겠습니다.
예제코드
- 커맨드 패턴을 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
'C#' 카테고리의 다른 글
[C#] EF Core 마이그레이션을 위한 패키지 관리자 콘솔 명령 (0) | 2023.03.03 |
---|---|
[gRPC] gRPC 서버 측 인터셉터 (0) | 2023.03.02 |
DevExpress Chart 기능 POC - Annotation 표시 방법 1 (0) | 2023.02.28 |
[C#] 도메인 주도 설계 - 데이터의 무결성 유지하기 (0) | 2023.02.27 |
[C#] C# 코딩 규칙(C# 프로그래밍 가이드) (0) | 2023.02.26 |
이 글을 공유하기