Mục lục
Java constructor là cách thông thường mà chúng ta có thể khởi tạo một instance từ một class. Với mỗi class cụ thể sẽ có những thuộc tính mà bắt buộc phải có giá trị, hay dựa vào một điều kiện cụ thể để khởi tạo các instance khác nhau v.v Chúng ta có thể đạt được điều tương tự với static factory method.
Trong bài viết này chúng ta sẽ cùng nhau tìm hiểu các đặc điểm nổi bật của static factory method so với constructor.
Những ưu điểm của static factory method so với constructor
Trong cuốn sách “Effective Java” tác giả Joshua Bloch đã nêu một số ưu điểm của static factory method như sau:
- Constructor không có tên, vì vậy nó không thể hiện đầy đủ ý nghĩa của một constructor trong vô số constructor tồn tại trong một class. Chúng ta phải tuân thủ theo cách đặt tên của thứ tự của tham số đầu vào để khởi tạo object như mong muốn. Trái ngược, static factory method có tên và có thể thể hiện đầy đủ ý nghĩa của nó, dựa vào đó mà chúng ta có thể đoán chính xác những gì nó sẽ làm để khởi tạo một object.
- Static factory method có thể trả về các object cùng kiểu dữ liệu (cùng implement interface, subtype), vì vậy chúng ta có thể linh hoạt hơn trong việc khởi tạo object.
- Static factory method có thể khởi tạo các thuộc tính bắt buộc cho từng instance cụ thể, nếu công việc này diễn ra bên trong constructor có thể gây ảnh hưởng đến các mục đích ban đầu của constructor là chỉ được dùng để khởi tạo object chứ không phải dùng để triển khai thêm các business logic của chúng ta.
- Static factory method có thể dùng để kiểm soát việc khởi tạo các instance, điển hình như singleton pattern dùng static factory method nhầm đảm bảo chỉ khởi tạo duy nhất một instance trong toàn bộ ứng dụng.
Áp dụng static factory method trong Java
Trong Java, có rất nhiều nơi sử dụng factory method để khởi tạo object mà mình sẽ dẫn chứng một số ví dụ trong bộ JDK được nhà phát triển phát hành.
String class
Mặc dù String là một kiểu dữ liệu đặc biệt được Java tối ưu rất nhiều nhờ vào việc sử dụng spring pool để reused lại các string object đã được khởi tạo sẵn. Tuy nhiên chúng ta hoàn toàn có thể sử dụng String constructor để khởi tạo một string object mới.
String value = new String("Deft");
Ngoài ra, String cũng cung cấp một static factory method có thể dùng để khởi tạo một string object là valueOf().
String value1 = String.valueOf(1); String value2 = String.valueOf(1.0L); String value3 = String.valueOf(true); String value4 = String.valueOf('a');
Optional class
Ngoài ra Optional class cũng sử dụng rất nhiều static factory method để khởi tạo Optional instance.
Optional<String> value1 = Optional.empty(); Optional<String> value2 = Optional.of("Deft"); Optional<String> value3 = Optional.ofNullable(null);
Custom Static factory method
Chúng ta hoàn toàn có thể tạo ra những static factory method cho riêng mình dùng trong các trường hợp cụ thể.
Giả sử mình có User class với một constructor đầy đủ các tham số đầu vào và các hàm getter, setter, toString được triển khai đầy đủ.
public class User { private final String name; private final String email; private final String country; public User(String name, String email, String country) { this.name = name; this.email = email; this.country = country; } // standard getters / toString }
Nếu mình muốn khởi tạo một user với một country mặc định thì mình có thể tạo static factory method sau
public static User createVNUser(String name, String email) { return new User(name, email, "VietNam"); }
Và bạn có thể khởi tạo một User instance với quốc gia mặc định là Việt Nam như sau:
User user = User.createWithDefaultCountry("Deft", "[email protected]");
Logic trong constructor
Giả sử chúng ta có một chức năng mới yêu cầu ghi log lại mỗi khi một User được tạo, nếu đang sử dụng constructor để khởi tạo User instance thì có thể bạn sẽ phải triển khai log trong constructor như vậy sẽ vi phạm Single Responsibility Principle. Constructor của chúng ra chứa thêm logic trong khi nhiệm vụ duy nhất của nó chỉ là khởi tạo giá trị cho các thuộc tính.
Chúng ta có thể di chuyển việc log lại dữ liệu đối với mỗi user tạo mới thông qua static factory method.
public class User { private static final Logger LOGGER = Logger.getLogger(User.class.getName()); private final String name; private final String email; private final String country; // các hàm constructors / getters public static User createWithLoggedInstantiationTime( String name, String email, String country) { LOGGER.log(Level.INFO, "Creating User instance at : {0}", LocalTime.now()); return new User(name, email, country); } }
Quản lý việc khởi tạo instance trong static factory method
Như đã đề cập trước đó chúng ta có thể sử dụng factory method để quản lý việc khởi tạo các instance. Giả sử như chúng ta muốn triển khai User class là một singleton class.
public class User { private static volatile User instance = null; // các hàm constructors / getters, thuộc tính public static User getSingletonInstance(String name, String email, String country) { if (instance == null) { synchronized (User.class) { if (instance == null) { instance = new User(name, email, country); } } } return instance; } }
Nguồn
https://www.baeldung.com/java-constructors-vs-static-factory-methods
https://stackoverflow.com/questions/929021/what-are-static-factory-methods