Strategy Pattern là một thuật toán dễ hiểu, dễ ứng dụng và vô cùng mạnh mẽ. Đây là thuật toán đầu tiên mà tôi viết trong 1 loạt bài tìm hiểu về Design Patterns.
Định nghĩa
Một định nghĩa không chính thức về Strategy Pattern (Phương pháp chiến lược) là:Định nghĩa tập hợp các thuật toán, đóng gói từng thuật toán lại, và làm chúng hoán đổi lẫn nhau. Strategy cho phép thuật toán biến đổi độc lập khi người dùng sử dụng chúng.(1)Một cách hiểu đơn giản hơn: Đây là mẫu thiết kế giúp bạn trừu tượng hóa những hành vi (behavior, method, function) của một đối tượng bằng cách đưa ra những cài đặt vào những lớp khác nhau.
Giả sử chúng ta nhận được yêu cầu viết 1 chương trình để đọc file txt. Rồi sau đó yêu cầu phát sinh, chúng ta phải viết các hàm đọc file csv, xls… Chúng ta không thể viết như thể viết các method lần lượt là:
public void ReadText(){…}
public void ReadExcel(){…}
…
Phương pháp này giúp bạn giải quyết nhanh vấn đề trong bài toán nhỏ. Nhưng 1 khi project của bạn lớn theo từng ngày, bạn không thể viết theo kiểu lập trình thủ tục này được. Nó tiềm ẩn nhiều rủi ro và vi phạm nguyên tắc hướng đối tượng: Open For Extension and Close For Modification.
Bạn thử tưởng tượng xem, nếu chương trình bạn có khả năng đọc tới 100 files, bạn sẽ có 100 methods, và mình nghĩ là bạn sẽ rối và khó quản lý chúng.
Khi nào sử dụng Strategy Pattern
- Nhiều class có liên quan với nhau chỉ khác nhau về hành vi. Dựa vào Strategy Pattern, ta xây dựng được 1 class cấu hình với 1 trong các hành vi đó.
- Bạn cần biến thể khác nhau của 1 thuật toán.
Ví dụ: Trước đây bạn dùng thuật toán MD5 để mã hóa dữ liệu. Sau này do nhu cầu thay đổi, bạn muốn dùng thuật toán SHA1 nhưng vẫn muốn giữ lại MD5. - Tạo giao diện sử dụng đơn giản, tránh đưa ra những chi tiết không cần thiết cho người sử dụng.
- Một class với nhiều hành vi, và sẽ xuất hiện nhiều lần trong câu lệnh điều kiện. Thay vì chọn lựa behaviour khi thi hành, di chuyển các nhánh liên quan vào trong lớp Strategy của riêng nó.
Ứng dụng
Đây là bài ví dụ của anh Nguyễn Văn Thoại, mình xin mượn đỡ ý tưởng để minh họa:Giả sử tôi có 1 cô bạn nữ. Vào những ngày cuối tuần, tôi thường dẫn cô ấy đi chơi giải trí. Tất nhiên đi chơi phải có chiến lược (strategy), nếu không thì sớm gặp thất bại. Tôi đã vạch sẵn trong đầu (server), hàng tá kế hoạch đi chơi (many algorithms). Tham khảo nguyên nhân thất bại 2 lần trước:
- Lần 1: Tôi chỉ cài đặt 1 hàm đi chơi, và mỗi lần muốn tìm chỗ mới, tôi ngồi sửa hàm. Sửa kịp thì không sao. Sửa không kịp thì tới ngày đi chơi, tôi đành cancel. Phương pháp này chiếm tỉ lệ thất bại hơn 75%.
- Lần 2: Tôi quyết định thay đổi hàm đi chơi. Thay vì viết 1 hàm, tôi viết hơn sẵn 1 chục hàm. Tới khi gặp cô ấy, tôi đưa ra nhiều lời đề nghị, nhưng hình như cô ấy không vui vì tôi luôn áp đặt nhiều cách đi chơi mà cô ấy không thích. Tỷ lệ thành công 50-50
Để đơn giản, tôi quyết định dùng 1 hàm duy nhất là Đi Chơi() để bạn ấy khỏi lựa chọn nhiều (lựa chọn về nhà :(( là tiêu). Để bạn ấy tùy hứng theo tâm trạng mà chọn tiết mục đi chơi nào, tôi quyết định cài đặt sẵn 1 số ý tưởng (implement interface), rồi tùy theo tình huống (runtime), cô ấy sẽ quyết định sẽ chọn Cách đi chơi nào (behaviour). Oh, tôi sung sướng thốt lên: Eureka. (Ý đừng có nhìn tôi tiếp nha).
Kế hoạch đi chơi |
Hàm Dẫn Bạn Gái Đi Chơi có duy nhất 1 phương thức: Đi chơi. (Đảm bảo tính đơn giản).
public interface ICachDiChoi { void DiChoi(); }
public class DiUongNuoc: ICachDiChoi { public void DiChoi() { Console.WriteLine("Di uong nuoc"); } }...
public class AnBinhTrong: IHuman { private readonly ICachDiChoi _cachDiChoi; public AnBinhTrong(ICachDiChoi cachDiChoi) { _cachDiChoi = cachDiChoi; } public void DanBanGaiDiChoi() { _cachDiChoi.DiChoi(); } }
class Program { static void Main(string[] args) { var abt = new AnBinhTrong(new DiUongNuoc()); abt.DanBanGaiDiChoi(); } }Source code: Mediafire
--------------
1: Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it
Nhận xét
Đăng nhận xét