Mẫu lệnh
Thuộc phần Lập trình hướng đối tượng trong hành trình Dart của Coddy. Bài học 94 trên 110.
Mẫu Command đóng gói một yêu cầu thành một đối tượng, cho phép bạn truyền các yêu cầu khác nhau làm tham số cho các bên gọi, xếp hàng các thao tác hoặc hỗ trợ chức năng hoàn tác. Thay vì gọi trực tiếp một phương thức, bạn đóng gói hành động trong một đối tượng lệnh để có thể lưu trữ, truyền đi hoặc thực thi sau.
Mẫu thiết kế này gồm ba thành phần chính: giao diện Command với phương thức execute, Concrete Commands triển khai các hành động cụ thể và Receiver thực hiện công việc thực tế. Một Invoker tùy chọn kích hoạt các lệnh mà không cần biết chúng làm gì:
abstract class Command {
void execute();
}
class Light {
void turnOn() => print('Light is ON');
void turnOff() => print('Light is OFF');
}
class TurnOnCommand implements Command {
final Light light;
TurnOnCommand(this.light);
@override
void execute() => light.turnOn();
}
class TurnOffCommand implements Command {
final Light light;
TurnOffCommand(this.light);
@override
void execute() => light.turnOff();
}
class RemoteControl {
Command? _command;
void setCommand(Command command) {
_command = command;
}
void pressButton() {
_command?.execute();
}
}
void main() {
var light = Light();
var remote = RemoteControl();
remote.setCommand(TurnOnCommand(light));
remote.pressButton(); // Đèn đã bật
remote.setCommand(TurnOffCommand(light));
remote.pressButton(); // Đèn đã tắt
}RemoteControl không biết gì về đèn, nó chỉ thực thi lệnh được giao. Sự tách biệt này giúp bạn dễ dàng thêm lệnh mới mà không cần sửa đổi đối tượng gọi. Mẫu Command rất phù hợp để triển khai hệ thống hoàn tác/làm lại, hàng đợi tác vụ hoặc ghi macro, khi bạn cần xem hành động là các đối tượng hạng nhất.
Thử thách
DễHãy cùng xây dựng một 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 đóng gói các thao tác với văn bản thành đối tượng lệnh, cho phép trình soạn thảo thực hiện nhiều hành động mà không cần biết chi tiết của từng thao tác. Đây là một trường hợp sử dụng kinh điển của mẫu Command, hãy hình dung như hệ thống hoàn tác/làm lại, trong đó mỗi hành động được lưu dưới dạng một đối tượng.
Bạn sẽ sắp xếp code vào hai tệp:
commands.dart: Tạo hệ thống lệnh tại đây. Bắt đầu bằng một lớp trừu tượngCommandkhai báo phương thứcexecute(). Sau đó, tạo lớpTextDocument(bên nhận) có trườngString content(ban đầu để trống) và ba phương thức:append(String text)- thêm văn bản vào cuối nội dungclear()- đặt nội dung thành chuỗi rỗngshow()- in raDocument: [content]
TextDocument:AppendCommand- nhận một tài liệu và văn bản trong hàm khởi tạo, rồi thêm văn bản khi được thực thiClearCommand- nhận một tài liệu trong hàm khởi tạo, rồi xóa nội dung khi được thực thiShowCommand- nhận một tài liệu trong hàm khởi tạo, rồi hiển thị nội dung khi được thực thi
TextEditor(bên gọi) để lưu danh sách lệnh. Lớp này cần có phương thứcaddCommand(Command command)để xếp lệnh vào hàng đợi và phương thứcrunAll()để thực thi tất cả lệnh đã xếp hàng theo đúng thứ tự.main.dart: Nhập tệp lệnh và minh họa mẫu Command. Tạo mộtTextDocumentvà mộtTextEditor. Xếp các lệnh sau vào hàng đợi theo thứ tự: thêmHello, thêmWorld, hiển thị tài liệu, thêm!, hiển thị lại, xóa tài liệu rồi hiển thị lần cuối. Sau đó, chạy tất cả lệnh đã xếp hàng.
Hãy để ý rằng TextEditor không biết gì về việc thêm, xóa hay hiển thị. Nó chỉ thực thi những lệnh được giao. Sự tách biệt này chính là cốt lõi của mẫu Command!
Đầu ra dự kiến:
Document: Hello World
Document: Hello World!
Document: Tự làm thử
import 'commands.dart';
void main() {
// TODO: Tạo một instance của TextDocument
// TODO: Tạo một instance của TextEditor
// TODO: Xếp các lệnh sau vào hàng đợi theo thứ tự:
// 1. Thêm "Hello"
// 2. Thêm " World"
// 3. Hiển thị tài liệu
// 4. Thêm "!"
// 5. Hiển thị lại tài liệu
// 6. Xóa nội dung tài liệu
// 7. Hiển thị tài liệu lần cuối
// TODO: Chạy tất cả lệnh trong hàng đợi
}
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
1Nền tảng về lập trình hướng đối tượng
Tệp bên ngoàiThư viện và câu lệnh importGiới thiệu về lập trình hướng đối tượngLớp và đối tượngTừ khóa thisPhương thứcBiến thể hiệnKiến thức cơ bản về hàm khởi tạoÔn tập: Máy tính đơn giản4An toàn với giá trị null
Giới thiệu về an toàn với giá trị nullCó thể null và không thể nullToán tử ? và !Từ khóa late và an toàn với giá trị nullToán tử xử lý nullAn toàn với giá trị null trong lớpÔn tập: hệ thống hồ sơ người dùng7Lớp trừu tượng và interface
Lớp trừu tượngPhương thức trừu tượngInterface trong DartInterface ngầm địnhTriển khai và kế thừaNhiều interfaceÔn tập: Máy tính hình10Bộ sưu tập và kiểu tổng quát
Tổng quan về List, Set và MapBộ sưu tập an toàn về kiểuLớp tổng quátPhương thức tổng quátRàng buộc kiểu tổng quátIterable và IteratorÔn tập: Lưu trữ tổng quát2Hàm khởi tạo trong Dart
Hàm khởi tạo mặc địnhHàm khởi tạo có tênDanh sách khởi tạoHàm khởi tạo hằng sốHàm khởi tạo nhà máyHàm khởi tạo chuyển hướngÔn tập: Trình tạo hình dạng5Đóng gói
Thành viên công khai và riêng tưQuy ước tiền tố _Tính riêng tư ở cấp thư việnTìm hiểu sâu về getter và setterChe giấu thông tinÔn tập: hồ sơ học sinh8Mixin
Giới thiệu về MixinTạo MixinSử dụng nhiều MixinTừ khóa on trong MixinMixin và kế thừaMixin và giao diệnÔn tập: hệ thống động vật3Thuộc tính của lớp
Thành viên instance và thành viên tĩnhTrường final và constBiến latePhương thức và trường tĩnhGetter và setterÔn tập: trình quản lý tài khoản ngân hàng6Kế thừa
Kế thừa cơ bảnTừ khóa superGhi đè phương thứcChú thích @overrideTừ khóa lớp finalHàm khởi tạo và kế thừaÔn tập: phân cấp nhân viên9Đa hình
Khái niệm cơ bản về đa hìnhĐa hình qua giao diệnKiểm tra kiểu lúc chạyToán tử is và asTừ khóa hiệp biếnÔn tập: Bộ xử lý thanh toán12OOP bất đồng bộ
Future và async/awaitCơ bản về StreamBộ điều khiển StreamHàm khởi tạo bất đồng bộBất đồng bộ trong phương thức lớpÔn tập: Trình lấy dữ liệu15Cá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ổng hợpMẫu RepositoryTự luyện tập: Trình biên dịch Dart online