12/08/2018, 14:45

Sử dụng factory method thay cho contructor trong java.

Cách thức thông thường đối với một class là cho phép một client tạo một instance của chính class đó thông qua một public constructor, Một class có thể cung cấp một public static factory method, nó đơn giản là một static method trả về một instance của class. Dưới đây là một ví dụ đơn giản từ Boolean ...

Cách thức thông thường đối với một class là cho phép một client tạo một instance của chính class đó thông qua một public constructor, Một class có thể cung cấp một public static factory method, nó đơn giản là một static method trả về một instance của class. Dưới đây là một ví dụ đơn giản từ Boolean (kiểu boxed primitive của boolean). Method này chuyển đổi một dữ liệuboolean thành một đối tượng Boolean:

public static Boolean valueOf(boolean b) {
       return b ? Boolean.TRUE : Boolean.FALSE;
}

Chú ý rằng static factory method không giống Factory Method pattern trong Design Patterns[Gamma95, p. 107]. Static factory method không có mô tả tương tự nào trong Design Patterns.

Một class có thể cung cấp cho các client của nó các factory method thay cho các public constructor điều này đều có những ưu điểm và nhược điểm sẽ được liệt kê sau đây

Ưu điểm thứ nhất của static factory method, không giống constructor chúng đều có tên.

Các parameter cho một constructor nó sẽ không có bất kỳ mô tả nào cho đối tượng được trả về, một statci factory method có một tên hợp lý nó sẽ dễ dàng được sử dụng và client code sẽ dễ dàng đọc hơn. Ví dụ, constructor BigInteger(int, int, Random), nó sẽ return một số nguyên tố BigInteger sẽ tốt hơn nếu thể hiện như một static factory có tên BigInteger.probablePrime. (phương thức này thậm chí đã được thêm từ phiên bản 1.4)

Một class có thể chỉ có một constructor, programmer biết rằng nên hạn chế cung cấp hai constructor với hàng loạt các prameter chỉ khác nhau về việc sắp xếp thứ tự, người sử dụng API sẽ không bao giờ nhớ được những constructor đó và sẽ dễ dàng dẫn đến sai lầm. Người đọc code để sử dụng các constructor đó sẽ không biết mục đích của constructor khi không tham khảo class document.

Bởi vì static factory method có tên do vậy nó sẽ không bị những hạn chế trên. Trong trường hợp một class yêu cầu nhiều constructor với cùng signature, thay thế các constructor với các static factory method phải cẩn thận trong việc lựa chọn tên sao cho thấy sự khác biệt một các rõ ràng giữa chúng.

Ưu điểm thứ hai của static factory method, không giống constructor chúng không cần thiết tạo ra một object mới với mỗi lần được gọi.

Sự linh hoạt của một application là nó có một API có thể trả về các đối tượng mà không cần phải public class của nó. Ẩn việc cài đặt class là cách làm gọn nhẹ API, kỹ thuật này thường được gọi là inteface-based frameworks, khi các interface cung cấp kiểu trả về cho static factory method.

Ví dụ, Java Collections Framework có 33 việc cài đặt cho cho cho các interface collection của nó, cung cấp các colection không thể chỉnh sửa, các collection được sysnchronize … tất cả các việc cài đặt đều được thể hiện thông qua static factory method trong một noninstantiable class(java.util.Collections). các class của đối tượng trả về đều là nonpublic.

Collections Framework API nhỏ hơn rất nhiều so với việc chúng thể hiển thông qua 32 public class, không chỉ độ lớn của API được giảm suống, người dùng còn biết được đối tượng được trả về chính xác API chỉ định theo interface của đối tượng mà không cần đọc thêm class document của class cài đặt. Ngoài ra, sử dụng một static factory method yêu cầu client tham chiếu tới đối tượng trả về theo interface hơn là class cài đặt.

Không chỉ class của một đối tượng được trả về bởi một public static factory method là nonpublic, class có thể khác nhau tùy với việc sử dụng sự khác nhau này phụ thuộc vào các giá trị của các tham số truyền cho static factory method. Bất kỳ class nào có một subtype được khai báo cho cho kiểu trả về đều hợp lệ. Class của đối tượng trả về có thể khác nhau theo các lần release cho phép tăng cường khả năng maintain cũng như performance.

Class java.util.EnumSet, được giới thiệu trong phiên bản java 1.5, không có pubic constructor, chỉ có các static factory method. Chúng trả về một trong hai implement dựa vào kích thước của kiểu enum, nếu nó có 64 phần tử hoặc nhỏ hơn thì các static factory method trả về một thể hiện RegularEnumSet, nếu enum type có 65 phần tử hoặc nhiều hơn, các factory trả về một thể hiện JumboEnumSet.

Class của đối tượng được trả về bởi static factory method không cần thiết tồn tại trong thời gian method được viết bởi vì sự linh hoạt của static factory method là một dạng cơ bản của service provider framework, như Java Database Connectivity API (JDBC). mộtservice provider frameworks là một hệ thống bao gồm nhiều service provider cài đặt một service, và một hệ thống cài đặt cần thiết cho client của chúng được tách rời từ những cài đặt đó.

Có ba thành phần qua trọng của một service provider framework: một service interface, nó cung cung việc cài đặt, một provider reistration API, là hệ thống sử dụng để đăng ký việc cài đặt, cho phép client truy cập vào chúng; và một service access API, các client của nó sử dụng để đạt được một instance của service. service access API thông thường cho phép nhưng không yêu cầu client chỉ định một vài tiêu chí để lựa chọn provider. Nếu như không có bất kỳ một chỉ định nào, API sẽ trả về một instance của một cài đặt mặc định. service access API là flexible static factory là một dạng cơ bản của service provider framework.

Một thành phần thứ tư (Optional) của service provider frameworklà một service provider interface, nó cung cấp việc cài đặt để tạo các instance của của các service. Trong trường hợp của JDBC,Connection là một phần của sevice interface, DriverManager.registerDriver là provider registration API, DriverManager.getConnection là service access API, và Driver là service provider interface.

Có nhiều pattern của service provider framework. Ví dụng, service access API có thể trả về một service inteface hơn là một provider, sử dụng Adapter pattern. Dưới đây là một cài đặt đơn giản với một service provider interface và một provider mặc định.

// Service interface
   public interface Service {
       ... // Service-specific methods go here
}

// Service provider interface
   public interface Provider {
       Service newService();
}
   // Noninstantiable class for service registration and access
   public class Services {
       private Services() { }  // Prevents instantiation (Item 4)
       // Maps service names to services
       private static final Map<String, Provider> providers =
           new ConcurrentHashMap<String, Provider>();
       public static final String DEFAULT_PROVIDER_NAME = "<def>";

// Provider registration API
       public static void registerDefaultProvider(Provider p) {
           registerProvider(DEFAULT_PROVIDER_NAME, p);
}
public static void registerProvider(String name, Provider p){
           providers.put(name, p);
       }
       // Service access API
       public static Service newInstance() {
           return newInstance(DEFAULT_PROVIDER_NAME);
       }
       public static Service newInstance(String name) {
           Provider p = providers.get(name);
           if (p == null)
               throw new IllegalArgumentException(
                   "No provider registered with name: " + name);
           return p.newService();
       }
}

Ưu điểm thứ tư của factory method: chúng giảm sự giư thừa khi tạo một paramterized type instance.

Nếu như bạn sử dụng java 8 trở lên thì điều này sẽ không cần thiết nhưng đối với các phiên bản về trước khi bạn tạo một paramterized type instance thì bạn phải truyền đầy đủ kiểu của parameter ví dụ:

Map<String, List<String>> m = new HashMap<String, List<String>>():

với việc chỉ định dư như trên sẽ làm cho độ dài cũng như độ phức tạp của các kiểu parameter tăng lên. Với static factory compiler có thể chỉ ra kiểu parameter cho bạn nó được hiểu as type inference. Ví dụ, giả sử HashMap cung cấp một static factory như sau:

public static <K, V> HashMap<K, V> newInstance() {
       return new HashMap<K, V>();
}

Sau đó bạn chỉ cần thay thế phần định nghĩa parameterized dư thừa ở ví dụ phía trên:

Map<String, List<String>> m = HashMap.newInstance();

Nhược điểm chính của static factory method đó là class sẽ không public hoặc protected contructor.

Điều này đúng đối với một nonpublic class trả về các public static factory. Ví dụ không thể có subclass nào để thuận tiện cho việc cài đặt class trong Collections Framework. Nhưng chính điều này cũng khuyến khích các lập trình viên sử dụng composition thay cho inheritance.

Nhược điểm thứ hai của static factory method là chúng không thực sự phân biệt được với những static method khác.

Chúng không phải là một chuẩn trong tài liệu API, do vậy khá khó để chỉ ra làm thế nào khởi tạo một class được cung cấp bởi static factory method thay cho constructor. Javadoc tool có thể có vài draw attention cho static factory method. Bạn có thể giảm nhược điểm này của static factory method bằng cách sử dụng drawing attention hoặc sử dụng comment, một cách khác bạn có thể sử dụng naming convention. Dưới đây là một số common name được sử dụng cho static factory method:

  • valueOf : trả về một instance, có cùng value với parameter của nó.
  • of: một dạng ngắn gọn của valueOf,, được sử dụng trong EnumSet.
  • getInstance: trả về một instance được mô tả bởi các parameter nhưng không thể cho rằng nó cùng value với parameter. Trong trường hợp này là một sigleton, getInstance không có parameter và trả về duy nhất một instance.
  • newInstance: giống với getInstance, ngoại trừ newInstance đảm bảo rằng mỗi instance được trả về sẽ khác nhau.
  • getType: giống với getInstance, nhưng được sử dụng khi factory method trong một class khác. Type chỉ định kiểu của object được trả về trong factory method.
  • newType: giống với newInstance, nhưng được sử dụng khi factory method nhưng trong một class khác. Type chỉ định kiểu của object được trả về bởi factory method.

Static factory method và public constructor cả hai đều được sử dụng thông thường static factory thường được sử dụng khi bạn muốn tránh mọi tham chiếu tới public constructor do vậy khi sử dụng bạn cần cân nhắc kỹ lưỡng để sử dụng tốt hơn.

0