NỘI DUNG BÀI VIẾT
Mô tả chung
Java Singleton thuộc vào 1 trong 5 design pattern của nhóm Creational Design Pattern.
- Singleton là một mẫu thiết kế creational cho phép bạn đảm bảo rằng một lớp chỉ có một đối tượng thể hiện và cung cấp truy cập đối tượng này với phạm vi toàn ứng dụng.
- Singleton đảm bảo chỉ duy nhất môt thể hiện mới (new instance) được tạo ra và nó sẽ cung cấp cho bạn một phương thức để truy cập đến thực thể đó.
Singleton giải quyết các vấn đề như:
- Làm thế nào có thể được đảm bảo rằng một lớp chỉ có một đối tượng
- Làm thế nào có thể truy cập dễ dàng một thể hiện duy nhất của một lớp
- Làm thế nào để một lớp kiểm soát sự hiện thân của nó
- Làm thế nào có thể hạn chế số lượng các thể hiện của một lớp
Cấu trúc
Một Singleton Pattern thường là 1 class (Class Singleton) có các đặc điểm:
- Phương thức khởi tạo private để ngăn cản việc tạo thể hiện của class từ các class khác
- Biến private static của class, nó là thể hiện duy nhất của class.
- Có một phương thức là public static để trả về thể hiện của class.
Một số cách thức sử dụng Singleton
1. Eager initialization
Singleton Class được khởi tạo ngay khi được gọi đến. Đây là cách dễ nhất nhưng nó có một nhược điểm mặc dù instance đã được khởi tạo mà có thể sẽ không dùng tới. Ví dụ:
public class EagerInitializedSingleton { private static final EagerInitializedSingleton instance = new EagerInitializedSingleton(); //private constructor to avoid client applications to use constructor private EagerInitializedSingleton(){} public static EagerInitializedSingleton getInstance(){ return instance; } }
2. Static block initialization
Cách làm tương tự như Eager initialization chỉ khác phần static block cung cấp thêm option cho việc handle. Ví dụ:
public class StaticBlockSingleton { private static StaticBlockSingleton instance; private StaticBlockSingleton(){} //static block initialization for exception handling static{ try{ instance = new StaticBlockSingleton(); }catch(Exception e){ throw new RuntimeException("Exception occured in creating singleton instance"); } } public static StaticBlockSingleton getInstance(){ return instance; } }
3. Lazy Initialization
Là một cách làm mang tính mở rộng hơn so với 2 cách làm trên và hoạt động tốt trong từng thread đơn lẻ. Và tất nhiên vấn để xấu sẽ sảy ra nếu chúng ta đang dùng nó với multi thread.
Ví dụ:
public class LazyInitializedSingleton { private static LazyInitializedSingleton instance; private LazyInitializedSingleton(){} public static LazyInitializedSingleton getInstance(){ if(instance == null){ instance = new LazyInitializedSingleton(); } return instance; } }
4. Thread Safe Singleton
Thực chất với những gì ta đã nắm được từ Java basic thì đã nghĩ ngay đến để method getInstance
với synchronized
.
5. Bill Pugh Singleton Implementation
Với cách làm này bạn sẽ tạo ra static nested class
với vai trò 1 Helper khi muốn tách biệt chức năng cho 1 class function rõ ràng hơn.
Ví dụ:
public class BillPughSingleton { private BillPughSingleton(){} private static class SingletonHelper{ private static final BillPughSingleton INSTANCE = new BillPughSingleton(); } public static BillPughSingleton getInstance(){ return SingletonHelper.INSTANCE; } }
6. Using Reflection to destroy Singleton Pattern
Reflection được dùng để destroy tất cả các singletion ở trên mà chúng ta đã tạo ra nó. Ví dụ:
public class ReflectionSingletonTest { public static void main(String[] args) { EagerInitializedSingleton instanceOne = EagerInitializedSingleton.getInstance(); EagerInitializedSingleton instanceTwo = null; try { Constructor[] constructors = EagerInitializedSingleton.class.getDeclaredConstructors(); for (Constructor constructor : constructors) { //Below code will destroy the singleton pattern constructor.setAccessible(true); instanceTwo = (EagerInitializedSingleton) constructor.newInstance(); break; } } catch (Exception e) { e.printStackTrace(); } System.out.println(instanceOne.hashCode()); System.out.println(instanceTwo.hashCode()); } }
7. Enum Singleton
Khi dùng enum thì các params chỉ được khởi tạo 1 lần duy nhất, đây cũng là cách giúp bạn tạo ra Singleton instance. Tuy rằng cách làm của nó có phần cứng nhắc . Ví dụ:
public enum EnumSingleton { INSTANCE; public static void doSomething(){ //do something } }
8. Serialization and Singleton
Serialization là một kỹ thuật sắp xếp đối tượng cần lưu trữu một cách tuần tự. Dưới đây là quá trình đọc ghi dữ liệu khi tích hợp singleton. Ví dụ :
public class SerializableSingletonClass implements Serializable{ private static final long serialVersionUID = 1L; private int value; private String name; private SerializableSingletonClass(int value, String name) { if( value < 0 ) throw new IllegalArgumentException("Value may not be less than 0"); this.value = value; this.name = Validate.notNull(name, "Name may not be null"); } private static class SerializableSingletonHolder{ public static final SerializableSingletonClass INSTANCE; static { INSTANCE = new SerializableSingletonClass(0, "default"); } } private void readObject(ObjectInputStream stream) throws InvalidObjectException{ throw new InvalidObjectException("proxy required"); } private Object writeReplace(){ return new SerializationProxy(this); } private static class SerializationProxy implements Serializable{ private static final long serialVersionUID = 1L; public SerializationProxy(SerializableSingletonClass ignored) { } //Here is the question private Object readResolve(){ return SerializableSingletonHolder.INSTANCE; } } }