Design pattern - Vào thư viện tìm Factory method
Factory method và các design pattern nói chung, chắc hẳn anh em đã nghe đến cả trăm ngàn lần rồi. Nhưng liệu anh em đã bao giờ dừng lại và tự hỏi: “Các thư viện Java ứng dụng Factory Method như thế nào?” Đây sẽ là bài mở đầu trong một series thực chiến, nơi chúng ta sẽ cùng nhau khám phá cách mà các thư viện Java ứng dụng design pattern này, cũng như các biến thể thú vị của nó. Hãy cùng tôi bắt đầu hành trình tìm hiểu và áp dụng những kiến thức này vào thực tế!
I. Khái niệm và kiến trúc
Nguyên văn của các bác GoF trong cuốn Design patterns Elements of Reusable Object-Oriented Software
Define an interface for creating an object, but let subclasses decide which class to instantiate.
Factory Method lets a class defer instantiation to subclasses.GoF
Nhóm: Creational pattern
Structure tổng quát
Creator: Khai báo phương thức factoryMethod()
Concrete Creator: override factoryMethod()
trả về instance của một concreteProduct
Product: định nghĩa interface chung được sử dụng bởi các concreteCreator
Concrete Product: implement Product
interface
Như vậy có thể hiểu nôm na là đẩy vai trò khởi tạo cho các subclass.
Những tài liệu viết về định nghĩa và cấu trúc đã đủ nhiều rồi, anh em có thể tham khảo tài liệu của GoF, hoặc cuốn Head first Design pattern của O'Reilly.
Không dài dòng nữa, anh em cùng tôi đi vào thực hành tìm hiểu các cách áp dụng pattern này trong các Java library nào.
II. Cách các thư viện Java sử dụng Factory method
Các ví dụ dưới đây được tôi lấy trong cuốn Dive Into DESIGN PATTERNS
1. URLStreamHandlerFactory#createURLStreamHandler()
java.net.URLStreamHandlerFactory#createURLStreamHandler()
Ví dụ đầu tiên là URLStreamHandlerFactory
với phương thức createURLStreamHandler
trả về một URLStreamHandler
theo tham số đầu vào.
URLStreamHandlerFactory
tương ứng vớiCreator
FactoryMethod
tương ứng vớicreateURLStreamHandler
URLStreamHandler
tương ứng vớiProduct
DefaultFactory
tương ứng vớiConcreteCreator
.- Cuối cùng các
ConcreateProduct
sẽ là subclass củaURLStreamHandler
, bao gồm cácHandler
nằm trong các packagesun.net.www.protocol.file.Handler
sun.net.www.protocol.jar.Handler
sun.net.www.protocol.jrt.Handler
Structure được vẽ lại tương ứng như sau
Có thể thấy URLStreamHandlerFactory
ốp toàn bộ cấu trúc đã được định nghĩa của GoF, Client
chỉ cần gọi phương thức trên và truyền vào protocol mong muốn.
2. Calendar#getInstance()
java.util.Calendar#getInstance()
Factory method còn có các biến thể khác khi Creator
và Product
được gộp vào làm một như trong ví dụ của java.util.Calendar
.
Thay vì nhờ vả anh bạn Creator
như ví dụ trên, Calendar
tự tạo instance mới bằng cách sử dụng một static method - getInstance()
FactoryMethod()
chính là các hàm getInstance(Locale)
, chấp nhận đầu vào là locale
locale.getUnicodeLocaleType
=buddhist
trả về một instance mới củaBuddhistCalendar
locale.getUnicodeLocaleType
=japanese
trả về một instance mới củaJapaneseImperialCalendar
locale.getUnicodeLocaleType
=gregory
trả về một instance mới củaGregorianCalendar
Như vậy, khi cần Client
gọi phương thức Calendar.getInstance()
với tham số locale thích hợp sẽ nhận được một sub class của Calendar
tương ứng.
3. NumberFormat#getInstance()
java.text.NumberFormat#getInstance()
Ở ví dụ trên, Creator
đã được thay thế bởi một static method, không dừng ở đấy, có một biến thể khác khi Product
lúc này cũng không có các ConcreteProduct
con, mà factoryMethod()
chỉ đơn thuần trả về Product
với các thuộc tính mang tính phân loại.
Trường hợp của NumberFormat
, nó hoàn toàn không khởi tạo class con, nhưng lại khởi tạo các class con về mặt ý nghĩa, vẫn là number format nhưng được dùng cho mục đích format theo dạng số, dạng phần trăm hay format tiền tệ, etc..
4. CharSet#forName()
java.nio.charset.CharSet#forName()
Nếu anh em chưa biết, ngoài vai trò ẩn đi logic khởi tạo, Factory method
còn ứng dụng trong việc tối ưu tài nguyên sử dụng bằng cách trả về instance đã được khởi tạo thay vì khởi tạo instance mới.
CharSet.forName()
là một ví dụ của phương pháp này. Bằng cách sử dụng cache
, CharSet sẽ trả về instance đã được tạo trước đó nếu tham số đầu vào phù hợp, nếu không CharSet sẽ tạo mới và lưu vào cache để tối ưu tài nguyên cho các lần request sau.
III. Lời kết
Qua bài viết này, hy vọng anh em đã có một cái nhìn rõ ràng hơn về cách các thư viện Java sử dụng Factory Method
, từ những ví dụ kinh điển như URLStreamHandlerFactory
, Calendar
, đến những biến thể độc đáo như CharSet
hay NumberFormat
.
Việc hiểu rõ và nắm vững những pattern này không chỉ giúp chúng ta viết code linh hoạt hơn mà còn mở ra nhiều cách tiếp cận sáng tạo trong quá trình phát triển phần mềm.
Hãy tiếp tục theo dõi series để cùng khám phá thêm nhiều điều thú vị về các design pattern trong các thư viện Java khác nhé
IV. Tài liệu tham khảo
- Refactoring Guru: https://refactoring.guru/design-patterns
- Head first design pattern - O'Reilly
- Design patterns Elements of Reusable Object-Oriented Software - GoF
Nếu anh em cảm thấy bài viết hữu ích đừng ngần ngại click upvote cho bài viết, hoặc phát hiện ý nào chưa hợp lý hoặc cần giải thích thêm hãy comment cho tôi biết để cùng trao đổi nhé.
Anh em có thể tham khảo các bài viết khác của tôi tại Blog cái nhân hoặc kết nối với tôi qua Linkedin
All rights reserved