Mẫu Thiết Kế Command: Đóng Gói Yêu Cầu Thành Đối Tượng

Mẫu thiết kế Command (hay còn gọi là Mẫu Lệnh) là một trong những mẫu thiết kế hành vi, cho phép bạn đóng gói một yêu cầu dưới dạng một đối tượng. Mục tiêu chính của mẫu này là tách rời đối tượng tạo ra yêu cầu (Invoker) khỏi đối tượng thực hiện yêu cầu (Receiver). Sự tách biệt này mang lại tính linh hoạt cao, cho phép các yêu cầu có thể được tham số hóa, đưa vào hàng đợi, ghi lại nhật ký, hoặc hỗ trợ các tính năng như hoàn tác (undo) và làm lại (redo).

Mẫu Command thường được cấu thành từ bốn thành phần chính:

  1. Giao diện Lệnh (Command Interface): Định nghĩa một phương thức duy nhất để thực thi lệnh (thường là Execute()). Đây là bản hợp đồng mà tất cả các lệnh cụ thể phải tuân theo.
  2. Lệnh Cụ Thể (Concrete Command): Triển khai giao diện lệnh và liên kết một hành động cụ thể với một đối tượng thực thi. Nó lưu trữ tham chiếu đến đối tượng thực thi và tất cả các thông tin cần thiết để gọi phương thức của đối tượng đó.
  3. Đối tượng Thực thi (Receiver): Là lớp chứa logic nghiệp vụ thực sự. Nó biết cách thực hiện các thao tác khi một lệnh được kích hoạt. Receiver không cần biết gì về đối tượng Command hay Invoker.
  4. Đối tượng Gọi Lệnh (Invoker): Chịu trách nhiệm kích hoạt phương thức Execute() của đối tượng lệnh. Nó không quan tâm đến chi tiết lệnh cụ thể nào sẽ được thực thi hay đối tượng nào sẽ thực hiện nó; Invoker chỉ đơn giản giữ một tham chiếu đến đối tượng Command và yêu cầu nó thực thi.

Quy trình hoạt động của mẫu Command diễn ra như sau:

  1. Một đối tượng Lệnh Cụ Thể được khởi tạo, thường được cung cấp một tham chiếu đến Đối tượng Thực thi cụ thể mà nó sẽ tương tác.
  2. Đối tượng Lệnh Cụ Thể này sau đó được chuyển đến Đối tượng Gọi Lệnh.
  3. Khi Đối tượng Gọi Lệnh cần thực hiện yêu cầu, nó chỉ đơn giản gọi phương thức Execute() trên đối tượng Lệnh mà nó đang giữ.
  4. Lệnh Cụ Thể sau đó sẽ ủy quyền yêu cầu đến phương thức thích hợp của Đối tượng Thực thi đã được liên kết với nó.

Ưu điểm chính của mẫu Command bao gồm:

  • Tách biệt linh hoạt: Tách rời hoàn toàn đối tượng tạo yêu cầu khỏi đối tượng thực hiện, giúp hệ thống dễ dàng mở rộng và bảo trì hơn.
  • Hỗ trợ hoàn tác/làm lại: Các lệnh có thể được thiết kế để lưu trữ trạng thái, cho phép dễ dàng thực hiện hoàn tác (undo) hoặc làm lại (redo).
  • Xếp hàng và ghi log: Lệnh có thể được xếp hàng để thực hiện sau hoặc ghi log để phục hồi lỗi hệ thống.
  • Dễ dàng thêm lệnh mới: Chỉ cần tạo thêm các lớp lệnh cụ thể mới mà không cần thay đổi Invoker hay Receiver hiện có.

Mẫu Command đặc biệt hữu ích trong các tình huống sau:

  • Khi bạn cần tham số hóa các đối tượng với một hành động cần thực hiện.
  • Khi bạn cần hỗ trợ các thao tác như hoàn tác, làm lại hoặc ghi log các yêu cầu.
  • Khi bạn muốn tách biệt người gửi yêu cầu khỏi người thực hiện yêu cầu để tăng tính linh hoạt và khả năng mở rộng của hệ thống.

Để minh họa, hãy xem xét một ví dụ về việc ban hành các sắc lệnh trong một hệ thống quản lý triều đình.

Giao diện Đối tượng Thực thi (Receiver Interface)


/// <summary>
/// Giao diện cho các đối tượng có khả năng thực hiện nhiệm vụ.
/// </summary>
public interface IOfficialDutyPerformer
{
    /// <summary>
    /// Thực hiện nhiệm vụ nhậm chức.
    /// </summary>
    void AssumeOffice();

    /// <summary>
    /// Thực hiện nhiệm vụ triệu hồi về kinh.
    /// </summary>
    void ReturnToCapital();
}

Lớp Lệnh Trừu tượng (Abstract Command)


/// <summary>
/// Lớp cơ sở trừu tượng cho tất cả các lệnh.
/// </summary>
public abstract class GovernmentOrder
{
    protected IOfficialDutyPerformer _performer;

    public GovernmentOrder(IOfficialDutyPerformer performer)
    {
        _performer = performer;
    }

    /// <summary>
    /// Phương thức thực thi lệnh.
    /// </summary>
    public abstract void Execute();
}

Đối tượng Thực thi Cụ thể (Concrete Receiver)


/// <summary>
/// Một vị quan chức có khả năng thực hiện các nhiệm vụ.
/// </summary>
public class RoyalOfficial : IOfficialDutyPerformer
{
    public void AssumeOffice()
    {
        Console.WriteLine("Quan chức nhận lệnh nhậm chức tại vùng đất mới...");
    }

    public void ReturnToCapital()
    {
        Console.WriteLine("Quan chức nhận lệnh triệu hồi, trở về kinh thành báo cáo...");
    }
}

Các Lệnh Cụ thể (Concrete Commands)


/// <summary>
/// Lệnh nhậm chức.
/// </summary>
public class AppointmentOrder : GovernmentOrder
{
    public AppointmentOrder(IOfficialDutyPerformer performer) : base(performer) { }

    public override void Execute()
    {
        _performer.AssumeOffice();
    }
}

/// <summary>
/// Lệnh triệu hồi.
/// </summary>
public class RecallOrder : GovernmentOrder
{
    public RecallOrder(IOfficialDutyPerformer performer) : base(performer) { }

    public override void Execute()
    {
        _performer.ReturnToCapital();
    }
}

Đối tượng Gọi Lệnh (Invoker)


/// <summary>
/// Người truyền đạt lệnh, không biết chi tiết lệnh thực hiện gì.
/// </summary>
public class RoyalMessenger
{
    private GovernmentOrder _currentOrder;

    public void SetOrder(GovernmentOrder order)
    {
        _currentOrder = order;
    }

    /// <summary>
    /// Truyền đạt và thực thi lệnh.
    /// </summary>
    public void DeliverAndExecute()
    {
        if (_currentOrder == null)
        {
            Console.WriteLine("Không có lệnh nào để truyền đạt.");
            return;
        }
        _currentOrder.Execute();
    }
}

Mã Client (Người tạo và sử dụng lệnh)


/// <summary>
/// Bộ phận tạo và gửi các lệnh.
/// </summary>
public class ImperialBureau
{
    public void ConductOfficialBusiness()
    {
        // 1. Tạo đối tượng thực thi (Receiver)
        IOfficialDutyPerformer anOfficial = new RoyalOfficial();

        // 2. Tạo đối tượng truyền đạt lệnh (Invoker)
        RoyalMessenger messenger = new RoyalMessenger();

        // --- Giai đoạn 1: Bổ nhiệm ---
        Console.WriteLine("--- Ban hành sắc lệnh bổ nhiệm ---");
        // 3. Tạo lệnh cụ thể (AppointmentOrder) và liên kết với đối tượng thực thi
        GovernmentOrder appointmentCmd = new AppointmentOrder(anOfficial);
        // 4. Giao lệnh cho người truyền đạt
        messenger.SetOrder(appointmentCmd);
        // 5. Yêu cầu người truyền đạt thực thi lệnh
        messenger.DeliverAndExecute();

        Console.WriteLine("\nSau nhiều năm công tác...\n");

        // --- Giai đoạn 2: Triệu hồi ---
        Console.WriteLine("--- Ban hành sắc lệnh triệu hồi ---");
        // 3. Tạo lệnh triệu hồi cụ thể (RecallOrder) và liên kết với đối tượng thực thi
        GovernmentOrder recallCmd = new RecallOrder(anOfficial);
        // 4. Giao lệnh triệu hồi cho người truyền đạt
        messenger.SetOrder(recallCmd);
        // 5. Yêu cầu người truyền đạt thực thi lệnh triệu hồi
        messenger.DeliverAndExecute();
    }
}

Cách sử dụng


public class Program
{
    static void Main(string[] args)
    {
        ImperialBureau bureau = new ImperialBureau();
        bureau.ConductOfficialBusiness();

        Console.WriteLine("\nHoàn tất mô phỏng các sắc lệnh.");
        Console.ReadKey(); 
    }
}

Thẻ: C# DesignPatterns CommandPattern SoftwareArchitecture oop

Đăng vào ngày 24 tháng 10 lúc 21:02