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ắtMẫ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.
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ệnICommandtrong không gian tênTextEditorvớ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ộtDocumentvà chuỗitexttrong 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 raInserted: {text}DeleteCommand- nhận mộtDocumentvà mộtint counttrong hàm khởi tạo. Khi được thực thi, lệnh này xóacountký tự cuối cùng trong tài liệu và in raDeleted: {count} characters
Document.cs: Tạo lớpDocumenttrong 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óacountký 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ớpEditorđóng vai trò là đối tượng gọi lệnh. Lớp này cần có phương thứcSetCommand(ICommand command)và phương thứcExecuteCommand()để 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 (
inserthoặcdelete), 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}"
}
}
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
1Các khái niệm cơ bản về lập trình hướng đối tượng
Tệp bên ngoàiKhông gian tên và chỉ thịGiới thiệu về lớp và đối tượngTừ khóa thisPhương thức và tham sốTrường và thuộc tínhHàm khởi tạoKhởi tạo đối tượngÔn tập: Máy tính đơn giản4Kế thừa
Cú pháp kế thừa cơ bản (:)Từ khóa 'base'Từ khóa virtual và overrideLớp sealedLớp cơ sở 'object'Ôn tập: hệ phân cấp nhân viên7Tính năng nâng cao
Nạp chồng toán tửBộ lập chỉ mục (this[])Ghi đè ToString()Phương thức mở rộngÔn tập: Danh sách tùy chỉnh10Các mẫu thiết kế phần 1
Giới thiệu về mẫu thiết kếSingleton an toàn với luồngMẫu FactoryMẫu Observer (sự kiện)Mẫu Strategy2Thuộc tính và thành viên tĩnh
Thuộc tính tự độngThuộc tính chỉ đọc hoặc chỉ ghiTrường và phương thức tĩnhLớp tĩnhThành viên có thân biểu thức5Đa hình và interface
Đa hình lúc biên dịch và lúc chạyInterface và lớp trừu tượngNhiều interfaceInterface tường minhUpcasting và downcastingÔn tập: bộ tính toán hình học8Các khái niệm OOP nâng cao
Ưu tiên composition hơn kế thừaGenerics (lớp và phương thức)Delegate và sự kiệnAttribute và reflectionGiao diện IDisposable và câu lệnh usingKiến thức cơ bản về dependency injection11Các mẫu thiết kế phần 2
Mẫu lệnhMẫu bộ chuyển đổiMẫu trang tríMẫu phương thức khuôn mẫuMẫu trạng tháiMẫu tổ hợpTự luyện tập: Trình biên dịch C# online