Strategy Design Pattern - 21 Guns

Tung Vu Duc 🇻🇳
4 min readMay 5, 2020
Photo by Derwin Edwards from Pexels

Dạo gần đây mình hay nghe lại các bài hát us-uk ngày xưa, nghe đến 21 Guns — Green day thì tự nhiên muốn viết về Strategy pattern, tất nhiên nội dung bài hát thì không gì đến liên quan Strategy rồi, chỉ là cái tên làm mình liên tưởng tới thôi 😆. Dù sao thì đây cũng là một pattern rất hay, trong bài viết này hãy cùng mình tìm hiểu về nó nhé.

Definition

Strategy design pattern thuộc nhóm behavioral. Strategy design pattern định nghĩa một nhóm các thuật toán, đóng gói từng thuật toán, và chúng có thể thay thế cho nhau một cách dễ dàng. Strategy cũng giúp các thuật toán này độc lập với client sử dụng.

Pattern này làm mình liên tưởng tới mấy game bắn súng mà hồi còn ăn ngủ cùng game mình từng chơi như Counter Strike, Call of duty,… Khi bạn có thể mang cùng một lúc nhiều vũ khí khác nhau nhưng chỉ có thể sử dụng một vũ khí ở một thời điểm và có thể thay đổi lên tục trong suốt ván game tuỳ vào tình hình. Ôi nói tới lại ngứa tay quá, chắc viết xong bài này phải làm vài game mới được 😆… Hơi lạc đề chút xíu nhưng bạn hoàn toàn có thể hiểu Strategy theo cách đó.

Structure

Có thể thấy các thuật toán (ConcreteStrategyAConcreteStrategyB) cùng chia sẻ chung một Interface. Từ góc nhìn của client, nó không quan tâm và cũng không biết cụ thể thuật toán được sử dụng là gì, client chỉ giao tiếp với Interface, việc quyết định sử dụng thuật toán nào sẽ do một component khác lo.

Case study

Requirement

Ứng dụng của bạn cần load một list bài viết từ API server, để tăng performance cho ứng dụng cũng như tiết kiệm dung lượng cho người dùng , bạn sẽ load list bài viết này từ các nguồn khác nhau tuỳ vào từng trường hợp:

  • Nếu người dùng sử dụng Wifi API http://api.com/wifi/posts sẽ trả về 20 bài viết trên mỗi lần request.
  • Nếu người dùng sử dụng 3G API http://api.com/cellular/posts sẽ trả về 10 bài viết trên mỗi lần request
  • Nếu người dùng sử dụng không có kết nối internet sẽ lấy bài viết từ local

Define algorithms

protocol Loader {
func load(url: URL, completion: @escaping (Result<[FeedItem], Error>) -> Void)
}
class WifiLoader: Loader {
private let url: URL
init(url: URL){
self.url = url
}
func load(completion: @escaping (Result<[FeedItem], Error>) -> Void) {

}
}
class CellularLoader: Loader {
private let url: URL
init(url: URL){
self.url = url
}
func load(completion: @escaping (Result<[FeedItem], Error>) -> Void) {

}
}
class LocalLoader: Loader {
func load(completion: @escaping (Result<[FeedItem], Error>) -> Void) {

}
}

Usage

Không hề khó đúng không 🥳. Thuật toán cụ thể sẽ được truyền vào tuỳ theo kết nối internet của người dùng bằng cách sử dụng constructor injection. Tuy nhiên, một vấn đề với cách làm này là thuật toán sẽ không thể thay đổi ở runtime. Ví dụ, ban đầu người dùng không có kết nối internet, ta sẽ load bài viết từ local, tuy nhiên sau đó người dùng có kết nối internet trở lại, ta sẽ muốn các bài viết được lấy từ Wifi hoặc Cellular, nhưng với cách làm như trên thì không thể được. Hmm… Làm thế nào nhỉ ?

Ta có thể kết hợp sử dụng Composite pattern cùng với Strategy để giải quyết vấn đề trên một cách dễ dàng. Mình cũng đã có một bài viết về Composite, các bạn có thể tham khảo thêm ở đây:

Change algorithms in runtime

Usage

Conclusion

Vậy là chúng ta đã cùng tìm hiểu xong về Strategy rồi đấy 🥳. Mình thấy đây là một design pattern rất hay và được áp dụng cũng khá nhiều trong các dự án, hy vọng các bạn đã hiểu hơn về nó qua bài viết của mình. Hãy học và áp dụng vào công việc hàng ngày nhé.

Nếu cảm thấy bài viết có ích, đừng quên claps và ấn follow để xem được những bài viết mới của mình. Nếu có câu hỏi hoặc bất cứ đóng góp ý kiến gì hãy để lại ở phần comment, cảm ơn các bạn 🥰.

Tài liệu tham khảo:
-
Design Patterns: Elements of Reusable Object-Oriented Software (Gang of four)

--

--

Tung Vu Duc 🇻🇳

Passionate about writing good software. Contact me: 📮tungvuduc2805@gmail.com