Command pattern là gì ?
- Command pattern là một mẫu thiết kế mà chúng ta có thể sử dụng để biến một yêu cầu thành một đối tượng chứa tất cả thông tin về yêu cầu.
- Command pattern khá phổ biến trong C #, đặc biệt khi chúng ta muốn trì hoãn hoặc xếp hàng đợi việc thực hiện một yêu cầu hoặc khi chúng ta muốn theo dõi các hoạt động. Hơn nữa, chúng ta có thể hoàn tác tác chúng.
Lấy ví dụ thực tế ?
-
Command pattern bao gồm lớp Invoker, lớp Command / giao diện, các lớp lệnh Concrete và lớp Receiver. Trong ví dụ này chúng ta sẽ áp dụng Command pattern nhé.
-
Chúng ta sẽ viết một ứng dụng đơn giản, trong đó chúng ta sẽ sửa đổi giá của sản phẩm sẽ triển Command pattern.
-
Trước hết là khởi tạo class Product có 2 thuộc tính Name, Price. 2 method tăng IncreasePrice, giảm DecreasePrice giá sản phẩm và một method ToString để in ra giá sản phẩm hiện tại.
public 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}$.";
}
- Bây giờ, chúng ta tạo ra các thao tác yêu cầu vào một class đặc biệt gọi là class Command.
- Khởi tạo interface ICommand
public interface ICommand
{ void ExecuteAction();
}
- Tạo ra một enum đơn giản PriceAction chứa hai hành động tăng giảm giá sản phẩm
public enum PriceAction
{ Increase, Decrease
}
- Cuối cùng là class ProductCommand
public 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); } }
}
-
Như chúng ta thấy, lớp ProductCommand có tất cả thông tin về yêu cầu và dựa trên đó chúng ta thực thi hành động được yêu cầu.
-
Tiếp tục, chúng ta thêm class ModifyPrice, class này sẽ đóng vai trò là Invoker
public 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(); }
}
-
Class ModifyPrice có thể hoạt động với bất kỳ lệnh nào triển khai giao diện ICommand và lưu trữ tất cả các hoạt động
-
Bây giờ thử chạy chương trình xem sao
class Program
{ static void Main(string[] args) { 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); } private static void Execute(Product product, ModifyPrice modifyPrice, ICommand productCommand) { modifyPrice.SetCommand(productCommand); modifyPrice.Invoke(); }
}
- Kết quả chương trình
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$.
-
Mọi thứ hoạt động tốt, chúng ta theo dõi được sự biến động giá của sản phẩm.
-
Bây giờ chúng ta muốn Undo hành động này lại thí sẽ ntn ?
-
Chỉnh sửa interface ICommand
public interface ICommand
{ void ExecuteAction(); void UndoAction();
}
- Thêm method UndoAction trong class ProductCommand
public void UndoAction()
{ if (_priceAction == PriceAction.Increase) { _product.DecreasePrice(_amount); } else { _product.IncreasePrice(_amount); }
}
- Thêm method UndoActions trong class Invoker ModifyPrice
public void UndoActions()
{ foreach (var command in Enumerable.Reverse(_commands)) { command.UndoAction(); }
}
- Bây giờ thử lại xem nào. Chúng ta sẽ quan sát thấy nó sẽ lặp lại tất cả các thao tác từ danh sách và thực hiện thao tác ngược lại với thao tác được yêu cầu trước đó.
class Program
{ static void Main(string[] args) { 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); Console.WriteLine(); modifyPrice.UndoActions(); Console.WriteLine(product); } private static void Execute(Product product, ModifyPrice modifyPrice, ICommand productCommand) { modifyPrice.SetCommand(productCommand); modifyPrice.Invoke(); }
}
- Kết quả chương trình
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$. The price for the Phone has been increased by 25$.
The price for the Phone has been decreased by 50$.
The price for the Phone has been decreased by 100$.
Current price for the Phone product is 500$.
Cuối cùng thì nãy giờ chúng ta đã làm những gì ?
- Với Command Design Pattern, chúng ta tách các lớp gọi các phép toán từ các lớp thực hiện các phép toán này
- Ngoài ra, nếu chúng ta muốn thêm các thao tác mới, chúng ta không phải sửa đổi các lớp hiện có. Thay vào đó, chúng ta chỉ có thể thêm các lớp lệnh mới đó vào dự án của mình