Menu
CoddyTech

Mẫu lệnh

Thuộc phần Lập trình hướng đối tượng trong hành trình C# của Coddy. Bài học 56 trên 70.

Mẫu Command là một mẫu hành vi biến một yêu cầu thành một đối tượng độc lập chứa mọi thông tin về yêu cầu đó. Cách chuyển đổi này cho phép bạn truyền yêu cầu làm đối số cho phương thức, xếp chúng vào hàng đợi, ghi nhật ký hoặc hỗ trợ các thao tác có thể hoàn tác.

Mẫu này tách đối tượng gọi một thao tác khỏi đối tượng biết cách thực hiện thao tác đó. Mẫu gồm bốn thành phần chính: giao diện lệnh, các lệnh cụ thể, đối tượng nhận lệnh (đối tượng thực hiện công việc thực tế) và đối tượng gọi lệnh (đối tượng kích hoạt các lệnh).

public interface ICommand
{
    void Execute();
}

public class Light  // Bộ nhận lệnh
{
    public void TurnOn() => Console.WriteLine("Light is ON");
    public void TurnOff() => Console.WriteLine("Light is OFF");
}

public class TurnOnCommand : ICommand
{
    private Light _light;
    
    public TurnOnCommand(Light light) => _light = light;
    
    public void Execute() => _light.TurnOn();
}

public class TurnOffCommand : ICommand
{
    private Light _light;
    
    public TurnOffCommand(Light light) => _light = light;
    
    public void Execute() => _light.TurnOff();
}

Bộ gọi lưu trữ và thực thi các lệnh mà không cần biết chúng làm gì:

public class RemoteControl  // Bộ gọi lệnh
{
    private ICommand _command;
    
    public void SetCommand(ICommand command) => _command = command;
    
    public void PressButton() => _command.Execute();
}

// Cách sử dụng
var light = new Light();
var remote = new RemoteControl();

remote.SetCommand(new TurnOnCommand(light));
remote.PressButton();  // Đèn đang bật

remote.SetCommand(new TurnOffCommand(light));
remote.PressButton();  // Đèn đang tắt

Mẫu Command đặc biệt hữu ích khi triển khai chức năng hoàn tác, ghi macro hoặc xếp hàng các thao tác để thực thi sau. Mỗi lệnh gói gọn mọi thứ cần thiết để thực hiện một hành động, giúp hệ thống linh hoạt và dễ mở rộng hơn.

challenge icon

Thử thách

Dễ

Hãy xây dựng hệ thống lệnh cho trình soạn thảo văn bản bằng mẫu Command. Bạn sẽ tạo một hệ thống trong đó các thao tác chỉnh sửa như chèn và xóa văn bản được đóng gói thành các đối tượng lệnh, cho phép trình soạn thảo thực thi chúng thông qua một giao diện thống nhất.

Bạn sẽ sắp xếp code trong ba tệp:

  • Command.cs: Định nghĩa giao diện ICommand trong không gian tên TextEditor với một phương thức duy nhất là Execute(). Sau đó, tạo hai lớp lệnh cụ thể:
    • InsertCommand - nhận một Document và chuỗi text trong hàm khởi tạo. Khi được thực thi, lệnh này nối văn bản vào tài liệu và in ra Inserted: {text}
    • DeleteCommand - nhận một Document và một int count trong hàm khởi tạo. Khi được thực thi, lệnh này xóa count ký tự cuối cùng trong tài liệu và in ra Deleted: {count} characters
  • Document.cs: Tạo lớp Document trong cùng không gian tên. Đây là đối tượng nhận lệnh, tức đối tượng thực hiện các thao tác trên văn bản. Lớp này cần có:
    • Một trường chuỗi riêng để lưu nội dung, được khởi tạo bằng chuỗi rỗng
    • Phương thức Append(string text) để thêm văn bản vào nội dung
    • Phương thức RemoveLast(int count) để xóa count ký tự cuối cùng. Nếu count vượt quá độ dài nội dung, hãy xóa toàn bộ nội dung
    • Phương thức GetContent() trả về nội dung hiện tại
  • Program.cs: Tạo lớp Editor đóng vai trò là đối tượng gọi lệnh. Lớp này cần có phương thức SetCommand(ICommand command) và phương thức ExecuteCommand() để chạy lệnh hiện tại. Trong code chính, hãy minh họa mẫu Command bằng cách xử lý một loạt thao tác chỉnh sửa.

Bạn sẽ nhận các đầu vào sau:

  • Số lệnh cần thực thi
  • Với mỗi lệnh: loại lệnh (insert hoặc delete), theo sau là văn bản cần chèn hoặc số ký tự cần xóa

Sau khi thực thi tất cả lệnh, hãy in nội dung cuối cùng của tài liệu trên một dòng mới theo định dạng Content: {content}.

Ví dụ, nếu đầu vào là:

4
insert
Hello
insert
 World
delete
3
insert
!

Đầu ra sẽ là:

Inserted: Hello
Inserted:  World
Deleted: 3 characters
Inserted: !
Content: Hello Wo!

Lưu ý rằng Editor (đối tượng gọi lệnh) không biết gì về việc thao tác văn bản, mà chỉ thực thi bất kỳ lệnh nào được giao. Các lệnh đóng gói cả hành động lẫn đối tượng nhận lệnh, giúp bạn dễ dàng thêm những thao tác mới như ReplaceCommand mà không cần thay đổi trình soạn thảo!

Tự làm thử

using System;
using TextEditor;

// TODO: Tạo lớp Editor (bộ gọi lệnh)
public class Editor
{
    // TODO: Thêm trường riêng tư để lưu lệnh hiện tại
    private ICommand _command;

    // TODO: Triển khai phương thức SetCommand
    public void SetCommand(ICommand command)
    {
        // TODO: Lưu lệnh
    }

    // TODO: Triển khai phương thức ExecuteCommand
    public void ExecuteCommand()
    {
        // TODO: Thực thi lệnh hiện tại
    }
}

class Program
{
    public static void Main(string[] args)
    {
        // Đọc số lượng lệnh
        int n = Convert.ToInt32(Console.ReadLine());

        // Tạo tài liệu (bộ nhận lệnh)
        Document document = new Document();

        // Tạo trình chỉnh sửa (bộ gọi lệnh)
        Editor editor = new Editor();

        // TODO: Xử lý từng lệnh
        for (int i = 0; i < n; i++)
        {
            string commandType = Console.ReadLine();

            // TODO: Dựa vào commandType, tạo lệnh phù hợp
            // Nếu là "insert": đọc văn bản và tạo InsertCommand
            // - Nếu là "delete": đọc số lượng và tạo DeleteCommand
            // Sau đó, đặt lệnh cho editor và thực thi lệnh
        }

        // TODO: In ra nội dung cuối cùng theo định dạng "Content: {content}"
    }
}
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch C# online