Factory Method Pattern – Giải thích đơn giản, dễ hiểu
Đã lâu không quay trở lại với series design pattern. Tiếp tục với Creational Pattern (pattern khởi tạo), đi cho hết luôn cả luồng. Bài viết này giới thiệu với anh em pattern Factory Method, một pattern khá nổi tiếng, được nhiều anh em biết tới.
Anh em ready chưa, bắt đầu ngay thôi!
1. Định nghĩa Factory Method pattern
Rất chi là khó hiểu nhưng luôn luôn là định nghĩa về pattern. Nếu anh em mới tìm hiểu về pattern này lần đầu thì đừng lo. Cứ lướt qua định nghĩa, đi xuống ví dụ, từng từng một sẽ rõ ràng ra.
Sau khi đọc hết và có cái hiểu cơ bản về pattern, quay lại đọc khái niệm đôi khi lại vỡ lẽ ra nhiều điều.
Factory Method is a creational design pattern that provides an interface for creating objects in a superclass, but allows subclasses to alter the type of objects that will be created. Factory method là design pattern khởi tạo cung cấp interface để khởi tạo các đối tượng (object) ở superclass, nhưng cho phép class phụ (subclasses) thay đổi kiểu của object sẽ được tạo ra.
Nghe ảo ma canada chưa, khởi tạo ở superclass nhưng lại cho phép thay đổi được kiểu của object cho phù hợp với subclass của mình. Vậy Factory Method pattern đã làm điều đó như thế nào?
2. Đặt vấn đề
Giả sử như anh em đang khởi tạo một ứng dụng liên quan tới giao vận (cũng có thể gọi là hệ thống quản lý giao vận – logistics management application). Đầu tiên, tiền còn ít nên anh em chỉ cố gắng xoay xở gom góp mua được con xe tải.
Quản lý giao vận bằng xe tải (truck), anh em sẽ tạo Truck class.
Một thời gian sau, các nhà đầu tư bần bần nhảy vô (nhầm, các agents) nhảy vào, ứng dụng của anh em trở nên phổ biến hơn cả Ahamove, Ninja Van. Một ông khách hàng to bự, bay vào nhờ anh em quản lý thêm giao vận trên biển (sea logistics).
Ngon, business phát triển rồi, nhưng giờ code thì sao?. Truck class thì viết đầy vào rồi, mà thêm Ship vào thì thay đổi mất code base. Mà không chỉ có mỗi sea, sau này còn có mở thị trường giao vận hàng không thì sao?.
3. Giải quyết với Factory Method pattern
Với Factory Method pattern, với bài toán ở trên, anh em mong muốn xây dựng một class sao cho cả Truck, Sea hoặc Airplane sau này có thể kế thừa. Mỗi lần xử lý đơn hàng là mỗi lần tạo transport.
Vậy phương thức createTransport sẽ tạo ra object chính xác cho từng loại hình vận chuyển.
- createTransport ở RoadLogistics sẽ return new Truck()
- createTransport ở SeaLogistics sẽ return new Ship()
Phương thức createTransport sẽ nằm ở SuperClass, subclass (Road, Sea) lúc này trả về các kiểu khác nhau.
Constructor khởi tạo lúc này di chuyển về Factory Method class (superclass), các class con thực hiện override lại, tất cả các phương thức vận chuyển đều sử dụng chung một interface, cái khác là kiểu trả về cho từng class
Ví dụ, cả Truck (xe tải) và Ship (thuyền) đều implement chung Transport interface. Trong interface rõ ràng khai báo method deliver(). Nhưng mỗi class lai hiện thức method này ở class của mình khác nhau. Truck thì di chuyển trên đường bộ, Ship thì di chuyển trên đường sông.
Factory Method lúc này, sau khi khởi tạo constructor ở class RoadLogistics sẽ trả về truck object, còn ShipLogistics sẽ trả về ship object. Rõ ràng nếu làm được vậy thì sau này có airplane hay hàng không vũ trụ gì cũng không là vấn đề.
4. Thực tế giải quyết bài toán
Rồi, giờ triển khai cụ thể với class diagram
- Số 1: Product sẽ khai báo interface, cái này là common interface tất cả các class đều sử dụng. doStuff() đây anh em có thể hiểu là vận chuyển, xác nhận đơn, giao thành công hoặc bất cứ thứ gì khác. Nói chung là có giao vận bằng hình thức nào thì nó cũng có các phương thức cần implement.
- Số 2: Concrete ProductA và Concrete ProductB sẽ implement inferface common, cụ thể làm gì thì viết vào đây. Ví dụ thuyền thì số km tính bằng hải lý, còn đường bộ thì tính bằng km
- Số 3: Class Creator sẽ khai báo các factory method trả về new product objects. Kiểu trả về ở đây khớp với Product interface là ok
- Số 4: Concrete Creators sẽ override các method base ở Factory với các kiểu trả về khác nhau.
5. Code ví dụ
Ví dụ giờ mình dùng cái Factory Method pattern để tạo một cái UI cross platform (đi qua nhiều platform). Cái UI này có một nút nhấn, từ đó mở được 2 loại:
- Web dialog
- Windows dialog
Lúc này ở interface sẽ có 2 method là render(), show cái button ra. Cái thứ hai là ấn vào để mở dialog.
Ở GUIFactory.java ta define interface tạo button và click. Render đổi qua thành createButton nha anh em. Đây là 2 common interface.
package refactoring_guru.abstract_factory.example.factories; import refactoring_guru.abstract_factory.example.buttons.Button; import refactoring_guru.abstract_factory.example.checkboxes.Checkbox; /** * Abstract factory knows about all (abstract) product types. */ public interface GUIFactory { Button createButton(); Button onClick(); }
Kế đến là MacOS (giả cái sử như là MacOS) có đi nha. Cái này sẽ implement GUIFactory.
package refactoring_guru.abstract_factory.example.factories; import refactoring_guru.abstract_factory.example.buttons.Button; import refactoring_guru.abstract_factory.example.buttons.MacOSButton; import refactoring_guru.abstract_factory.example.checkboxes.Checkbox; import refactoring_guru.abstract_factory.example.checkboxes.MacOSCheckbox; /** * Each concrete factory extends basic factory and responsible for creating * products of a single variety. */ public class MacOSFactory implements GUIFactory { @Override public Button createButton() { return new MacOSButton(); } @Override public function onClick() { return smt with click event } }
Phía Windows factory cũng tương tự
package refactoring_guru.abstract_factory.example.factories; import refactoring_guru.abstract_factory.example.buttons.Button; import refactoring_guru.abstract_factory.example.buttons.WindowsButton; import refactoring_guru.abstract_factory.example.checkboxes.Checkbox; import refactoring_guru.abstract_factory.example.checkboxes.WindowsCheckbox; /** * Each concrete factory extends basic factory and responsible for creating * products of a single variety. */ public class WindowsFactory implements GUIFactory { @Override public Button createButton() { return new WindowsButton(); } @Override public function onClick() { return smt with click event } } Footer
Xong phần Factory, giờ là lúc hiện thực Button cho từng class. Button là common interface có method paint() để render button.
package refactoring_guru.abstract_factory.example.buttons; /** * Abstract Factory assumes that you have several families of products, * structured into separate class hierarchies (Button/Checkbox). All products of * the same family have the common interface. * * This is the common interface for buttons family. */ public interface Button { void paint(); } Footer
Lúc này ở MacButton thì khoẻ rồi. Override method và implement những gì mình thích thôi
package refactoring_guru.abstract_factory.example.buttons; /** * All products families have the same varieties (MacOS/Windows). * * This is a MacOS variant of a button. */ public class MacOSButton implements Button { @Override public void paint() { System.out.println("You have created MacOSButton."); } }
Xong xuôi đâu vào đấy thì ở application còn làm gì nữa đâu. Cứ gọi factory là trả ra đúng object của button cần tạo, thích button thì button, thích checkbox thì có checkbox. Rồi từ đó cứ gọi ra các method implement là xong thôi.
package refactoring_guru.abstract_factory.example.app; import refactoring_guru.abstract_factory.example.buttons.Button; import refactoring_guru.abstract_factory.example.checkboxes.Checkbox; import refactoring_guru.abstract_factory.example.factories.GUIFactory; /** * Factory users don't care which concrete factory they use since they work with * factories and products through abstract interfaces. */ public class Application { private Button button; private Checkbox checkbox; public Application(GUIFactory factory) { button = factory.createButton(); checkbox = factory.createCheckbox(); } public void paint() { button.paint(); checkbox.paint(); } } Footer
6. Ưu nhược điểm
- Đầu tiên là với Factory pattern anh em sẽ hạn chế đụng độ giữa các class.
- Mỗi product giờ nằm ở một nơi, chia ra rõ ràng, dễ dàng hơn để maintain sau này.
- Open,close principles: Anh em thoải mái tạp thêm creator mới mà không cần đụng tới các methods, class cũ đã implement. ít bug hơn.
- Sử dụng Factory pattern đôi khi làm cho code trở nên khó hiểu hơn, phức tạp. Trường hợp có nhiều subclass.
7. Tham khảo
- Design Patterns – SourceMaking
- Adapter Pattern – Xoá nhoà khác biệt
- Mediator Design Pattern – Collaborate via me
Thank you for your time – Happy coding!
Tác giả: Kiên Nguyễn
Cảm ơn anh em đã dành thời gian để đọc bài. Anh em có thể tham khảo thêm tin tuyển dụng IT trên TopDev để phát triển sự nghiệp.
Xem thêm:
Một số custom hooks hay sử dụng cho React
AngularJS Là Gì? Khác Biệt Nào Giữa Angular Và Frontend Framework Khác
- F Framework nào tốt nhất cho dự án của bạn? – Checklist chi tiết
- K Kinh nghiệm xử lý responsive table hiệu quả
- S Stackoverflow là gì? Bí kíp tận dụng Stack Overflow hiệu quả
- 7 7 kinh nghiệm hữu ích khi làm việc với GIT trong dự án
- B Bài tập Python từ cơ bản đến nâng cao (có lời giải)
- B Bảo mật API là gì? Một số nguyên tắc và kỹ thuật cần biết
- H Hướng dẫn cài đặt và tự học lập trình Python cơ bản từ A-Z
- C Chinh Phục Phân Tích Dữ Liệu Với Pandas Trong Python: Hướng Dẫn Từng Bước
- D Display CSS là gì? Cách khai báo và sử dụng thuộc tính display trong CSS
- C Compiler là gì? Công việc cụ thể của một trình biên dịch