Mẫu Strategy
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 93 trên 110.
Mẫu Strategy pattern cho phép bạn định nghĩa một nhóm thuật toán, đóng gói từng thuật toán trong lớp riêng và thay thế chúng cho nhau khi chương trình chạy. Thay vì viết cứng hành vi trong một lớp, bạn truyền hành vi qua một giao diện, nhờ đó có thể thay đổi các chiến lược mà không cần sửa lớp sử dụng chúng.
Hãy xét một hệ thống thanh toán cần hỗ trợ nhiều phương thức thanh toán khác nhau. Thay vì dùng các câu lệnh điều kiện để xử lý từng phương thức, bạn định nghĩa một giao diện chiến lược và tạo các phần triển khai cụ thể:
abstract class PaymentStrategy {
void pay(double amount);
}
class CreditCardPayment implements PaymentStrategy {
@override
void pay(double amount) => print('Paid \$$amount with Credit Card');
}
class PayPalPayment implements PaymentStrategy {
@override
void pay(double amount) => print('Paid \$$amount with PayPal');
}
class ShoppingCart {
PaymentStrategy _paymentStrategy;
ShoppingCart(this._paymentStrategy);
void setPaymentStrategy(PaymentStrategy strategy) {
_paymentStrategy = strategy;
}
void checkout(double total) {
_paymentStrategy.pay(total);
}
}
void main() {
var cart = ShoppingCart(CreditCardPayment());
cart.checkout(99.99); // Đã thanh toán $99.99 bằng thẻ tín dụng
cart.setPaymentStrategy(PayPalPayment());
cart.checkout(49.99); // Đã thanh toán $49.99 bằng PayPal
}ShoppingCart không biết cũng không quan tâm cách thanh toán hoạt động, nó chỉ giao việc đó cho chiến lược hiện đang được thiết lập. Bạn có thể thêm phương thức thanh toán mới bằng cách tạo các lớp chiến lược mới mà không cần chỉnh sửa code của giỏ hàng. Mẫu này rất phù hợp khi bạn có nhiều cách thực hiện một hành động và muốn chọn hoặc chuyển đổi giữa chúng một cách linh hoạt.
Thử thách
DễHãy cùng xây dựng một hệ thống định dạng văn bản bằng mẫu Strategy nhé! Bạn sẽ tạo một hệ thống có thể áp dụng nhiều chiến lược định dạng khác nhau cho văn bản, cho phép người dùng chuyển đổi linh hoạt giữa các định dạng mà không cần thay đổi bộ xử lý văn bản cốt lõi.
Bạn sẽ sắp xếp code vào hai tệp:
formatting_strategies.dart: Tạo hệ thống chiến lược tại đây. Định nghĩa lớp trừu tượngFormattingStrategyvới phương thứcString format(String text). Sau đó, triển khai ba chiến lược cụ thể:UppercaseStrategy- chuyển toàn bộ văn bản thành chữ hoaLowercaseStrategy- chuyển toàn bộ văn bản thành chữ thườngTitleCaseStrategy- viết hoa chữ cái đầu tiên của mỗi từ (tách bằng dấu cách, viết hoa ký tự đầu tiên của mỗi từ, các ký tự còn lại viết thường)
TextProcessorđể lưu mộtFormattingStrategy. Lớp này cần có hàm khởi tạo nhận chiến lược ban đầu, phương thứcsetStrategy()để thay đổi chiến lược khi chương trình đang chạy và phương thứcprocess(String text)để áp dụng chiến lược hiện tại rồi trả về kết quả đã định dạng.main.dart: Nhập các chiến lược định dạng và minh họa cách mẫu Strategy cho phép chuyển đổi hành vi linh hoạt. Tạo mộtTextProcessorbắt đầu vớiUppercaseStrategy. Xử lý văn bảnhello worldrồi in kết quả ra. Sau đó, chuyển sangLowercaseStrategy, xử lýHELLO WORLDrồi in kết quả ra. Cuối cùng, chuyển sangTitleCaseStrategy, xử lýthe quick brown foxrồi in kết quả ra.
Ví dụ này cho thấy sức mạnh của mẫu Strategy: TextProcessor không cần biết chi tiết cách định dạng hoạt động. Nó chỉ chuyển việc xử lý cho chiến lược hiện được thiết lập, giúp bạn dễ dàng thêm các tùy chọn định dạng mới mà không phải sửa đổi bộ xử lý!
Kết quả đầu ra mong đợi:
HELLO WORLD
hello world
The Quick Brown FoxTự làm thử
import 'formatting_strategies.dart';
void main() {
// TODO: Tạo một TextProcessor, bắt đầu với UppercaseStrategy
// TODO: Xử lý văn bản 'hello world' và in ra kết quả
// TODO: Chuyển sang LowercaseStrategy
// TODO: Xử lý 'HELLO WORLD' và in ra kết quả
// TODO: Chuyển sang TitleCaseStrategy
// TODO: Xử lý 'the quick brown fox' và in ra kết quả
}
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ánTự luyện tập: Trình biên dịch Dart online