Ý nghĩa
Strategy Pattern là một mẫu thiết kế hành vi, cho phép bạn xác định một loạt các thuật toán và đặt mỗi thuật toán vào một lớp riêng biệt. Điều này cho phép các thuật toán có thể được thay đổi linh hoạt trong quá trình chạy.
Các trường hợp sử dụng
- Khi bạn muốn sử dụng các biến thể thuật toán khác nhau trong một đối tượng và muốn có khả năng thay đổi thuật toán trong quá trình chạy, bạn có thể sử dụng Strategy Pattern.
- Khi bạn có nhiều lớp tương tự nhau chỉ khác nhau một chút trong hành vi, bạn có thể sử dụng Strategy Pattern.
- Nếu thuật toán không quan trọng trong logic của ngữ cảnh, việc sử dụng mẫu này có thể tách biệt logic của lớp khỏi chi tiết thực hiện thuật toán.
Cấu trúc
Mô tả cấu trúc
- Ngữ cảnh (Context) duy trì một tham chiếu đến chiến lược cụ thể và chỉ giao tiếp với đối tượng thông qua giao diện chiến lược.
- Chiến lược (Strategy) là giao diện chung cho tất cả các chiến lược cụ thể, nó khai báo một phương thức để thực hiện chiến lược trong ngữ cảnh.
- Chiến lược cụ thể (Concrete Strategies) triển khai các biến thể khác nhau của thuật toán được sử dụng bởi ngữ cảnh.
- Khi ngữ cảnh cần thực thi thuật toán, nó gọi phương thức thực thi trên đối tượng chiến lược đã được kết nối. Ngữ cảnh không biết loại chiến lược và cách thực hiện thuật toán.
- Khách hàng (Client) tạo một đối tượng chiến lược cụ thể và chuyển nó cho ngữ cảnh. Ngữ cảnh cung cấp một phương thức thiết lập để cho phép khách hàng thay thế chiến lược liên quan trong quá trình chạy.
Mẫu code cấu trúc
Strategy : Định nghĩa giao diện chung cho tất cả các thuật toán (AlgorithmInterface). Context sử dụng giao diện này để gọi các thuật toán cụ thể được định nghĩa bởi ConcreteStrategy.
ConcreteStrategy : Thực hiện giao diện thuật toán (AlgorithmInterface) trong Strategy.
Context : Sử dụng một ConcreteStrategy để cấu hình. Giữ một tham chiếu đến đối tượng Strategy.
Khách hàng
Kết quả
Thuật toán A
Thuật toán B
Thuật toán C
Pseudocode
Trong ví dụ này, ngữ cảnh sử dụng nhiều chiến lược để thực hiện các phép tính khác nhau.
Ví dụ
Ví dụ sử dụng: Mẫu thiết kế Chiến lược rất phổ biến trong mã Java. Nó thường được sử dụng trong các framework khác nhau để cung cấp cho người dùng cách thay đổi hành vi mà không cần mở rộng lớp.
Từ Java 8 trở đi, lambda expression được hỗ trợ, đây là một cách đơn giản để thay thế cho mẫu thiết kế Chiến lược.
Dưới đây là một số ví dụ về mẫu thiết kế Chiến lược trong các thư viện Java cốt lõi:
- Cuộc gọi
java.util.Comparator#compare()
trongCollections#sort()
. javax.servlet.http.HttpServlet
: phương thứcservice()
và tất cả các phương thứcdoXXX()
nhận đối tượngHttpServletRequest
vàHttpServletResponse
làm tham số.javax.servlet.Filter#doFilter()
Cách nhận biết: Mẫu thiết kế Chiến lược có thể được nhận biết thông qua cách nó cho phép một đối tượng lồng nhau thực hiện công việc thực tế và thông qua việc cung cấp một trình thiết lập cho phép thay thế đối tượng đó bằng đối tượng khác.
Mối quan hệ với các mẫu khác
- Bridge Pattern, State Pattern và Strategy Pattern (bao gồm Adapter Pattern ở một mức độ) có giao diện rất tương tự. Trên thực tế, chúng đều dựa trên Composite Pattern - tức là giao việc cho các đối tượng khác nhau, nhưng mỗi mẫu giải quyết một vấn đề khác nhau. Mẫu không chỉ là công thức để tổ chức mã theo cách cụ thể, mà còn là cách để bạn trò chuyện với các nhà phát triển khác về vấn đề mà mẫu giải quyết.
- Command Pattern và Strategy Pattern trông giống nhau vì cả hai đều cho phép tham số hóa hành động thành đối tượng. Tuy nhiên, mục đích của chúng rất khác nhau.
- Bạn có thể sử dụng Command để chuyển đổi bất kỳ hoạt động nào thành một đối tượng. Tham số của hoạt động sẽ trở thành thuộc tính của đối tượng. Bạn có thể trì hoãn việc thực thi hoạt động, đưa hoạt động vào hàng đợi, lưu trữ lịch sử lệnh hoặc gửi lệnh đến dịch vụ từ xa.
- Mặt khác, Strategy thường được sử dụng để mô tả các cách khác nhau để thực hiện một công việc, cho phép bạn chuyển đổi thuật toán trong cùng một ngữ cảnh lớp.
- Decorator Pattern cho phép bạn thay đổi diện mạo của một đối tượng, trong khi Strategy Pattern cho phép bạn thay đổi bản chất của nó.
- Template Method Pattern dựa trên kế thừa: nó cho phép bạn thay đổi một phần của thuật toán bằng cách mở rộng trong các lớp con. Strategy Pattern dựa trên sự kết hợp: bạn có thể thay đổi một phần hành vi bằng cách cung cấp các chiến lược khác nhau cho cùng một ngữ cảnh đối tượng. Template Method hoạt động trên cấp lớp, vì vậy nó là tĩnh. Strategy hoạt động trên cấp đối tượng, cho phép thay đổi hành vi trong thời gian chạy.
- State Pattern có thể được coi là một mở rộng của Strategy Pattern]. Cả hai đều dựa trên cơ chế kết hợp: chúng đều thay đổi hành vi của đối tượng trong các tình huống khác nhau bằng cách ủy quyền một phần công việc cho đối tượng “trợ giúp”. Strategy làm cho các đối tượng này hoàn toàn độc lập với nhau, chúng không biết về sự tồn tại của các đối tượng khác. Tuy nhiên, State Pattern không giới hạn sự phụ thuộc giữa các trạng thái cụ thể và cho phép chúng tự thay đổi trạng thái trong các tình huống khác nhau.