Factory Pattern là một Pattern phổ biến trong OOP. Nhiệm vụ của Factory Pattern là giống như là một người môi giới quản lý và trả về các đối tượng theo yêu cầu, giúp cho việc khởi tạo đổi tượng một cách linh hoạt hơn. Mình sẽ hướng dẫn bài này dưới góc nhìn bóng đá cho bạn dễ hiểu.
Giả sử bạn là một huấn luyện viên MU muốn mua một tiền đạo, bạn sẽ phải đến các CLB để xem xét giò trước khi mua. Các CLB sẽ đưa cầu thủ ra cho bạn xem.
a. Trường hợp 1: Giả sử bạn rảnh muốn đi xem từng tiền đạo
Bước 1: Đương nhiên phải cầu thủ đó là tiền đạo - và muốn check chỉ số run và shoot
protocol Striker { func runIndex() func shootIndex() }
Bước 2: Gọi 2 ông ra, hai ông này phải conform các thuộc tính của một tiền đạo
+ Đầu tiên là CR7
public class Ronaldo { } extension Ronaldo: Striker { func runIndex() { print("Ronaldo Run: 98") } func shootIndex() { print("Ronaldo Shoot: 95") } }
+ Tiếp theo là Messi
public class Messi { } extension Messi: Striker { func runIndex() { print("Messi Run: 93") } func shootIndex() { print("Messi Shoot: 90") } }
Bước 3: Bạn đi xem giò Ronaldo và Messi.
public class Manager { public func viewRonaldo() { let ronaldo = Ronaldo() ronaldo.runIndex() ronaldo.shootIndex() } public func viewMessi() { let messi = Messi() messi.runIndex() messi.shootIndex() } } let manager = Manager() manager.viewRonaldo() manager.viewMessi()
+ Như vậy các bạn có thể thấy là việc thêm bớt Object sẽ phải chỉnh sửa thêm ở Manager. Nói nôm na là bây giờ chẳng lẽ muốn đi xem Neymar, Kane phải bay qua tận nơi để xem từng ông một. Như vậy quá mất thời giờ. Bây giờ đến cách 2, đó là tìm một ông cò (the broker), nhiệm vụ ổng sẽ đi thu thập thông tin của tất cả.
b. Trường hợp 2: Thông qua một môi giới
public class Broker { func viewStriker(striker: String) { var displayedStriker: Striker if striker.lowercased() == "ronaldo" { displayedStriker = Ronaldo() } else { displayedStriker = Messi() } displayedStriker.runIndex() displayedStriker.shootIndex() } } public class Manager { public func viewStrikers(striker: String) { let broker = Broker() broker.viewStriker(striker: striker) } } let manager = Manager() manager.viewStrikers(striker: "Ronaldo")
Như ở trên, lớp manager chỉ cần quan tâm/ gọi đến tên, object (Ronaldo hoặc Messi) mà không cần phải quan tâm đến các thuộc tính bên sâu (như run or shoot or bất cứ cái gì). Ở trường hợp 1, lớp biết rõ tường tận các Object có gì, làm gì.
Và thêm một điều hay nữa, nếu mà ta có thêm bớt gì (tức là tự dưng có thêm vài cầu hay) thì ta cũng không quan tâm. Mọi việc đã có ông cò lo, nhiệm vụ của ta chỉ là liên hệ cò rồi với danh sách cầu thủ trên tay, ta gọi ra từng cầu thủ để xem danh sách.
Tay cò này (Factory pattern) xuất hiện dẫn đến 1 ý tưởng mới cho việc khởi tạo các instance phù hợp với sự thay đổi request từ phía Client. Sử dụng Factory pattern sẽ có những ưu điểm sau:
Khởi tạo các Objects thông qua 1 protocol chung
Che giấu đi xử lí logic của việc khởi tạo đấy (cái này qua phần Clean Architecture các bạn sẽ thấy rõ vấn đề hơn)
Giảm sự phụ thuộc giữa các module, các logic với các class cụ thể, mà chỉ phụ thuộc vào protocol. (như trên, sẽ giải thích rõ hơn khi qua Clean Architecture).
Bài viết được tham khảo trên viblo.asia của bạn Chương tại đây.
Xem thông tin bạn Chương tại đây.