Tiếp theo phần 1, chúng ta hãy cùng cafedev làm demo về Singleton Design Pattern nhằm giúp ace dễ hiểu khi áp dụng trên các ngôn ngữ khác nhau. Ace có thể tham khảo thêm các bài khác tại series Design Pattern tại đây.

Pattern singleton là một trong những design patterns đơn giản nhất. Đôi khi chúng ta chỉ cần có một thể hiện của lớp mình, ví dụ như một kết nối DB duy nhất được chia sẻ bởi nhiều đối tượng vì việc tạo một kết nối DB riêng cho mọi đối tượng có thể tốn kém. Tương tự, có thể có một trình quản lý cấu hình hoặc trình quản lý lỗi duy nhất trong một ứng dụng xử lý tất cả các vấn đề thay vì tạo nhiều trình quản lý.

1. Định nghĩa:  

Singleton pattern là một design patterns hạn chế việc khởi tạo một lớp cho một đối tượng.  

Hãy xem các tùy chọn thiết kế khác nhau để triển khai một lớp như vậy. Nếu bạn xử lý tốt các biến lớp tĩnh và công cụ sửa đổi quyền truy cập thì đây không phải là một nhiệm vụ khó khăn.

2. Phương pháp 1: Thực hiện Cách Cổ điển 

/*
Cafedev.vn - Kênh thông tin IT hàng đầu Việt Nam
@author cafedevn
Contact: cafedevn@gmail.com
Fanpage: https://www.facebook.com/cafedevn
Group: https://www.facebook.com/groups/cafedev.vn/
Instagram: https://instagram.com/cafedevn
Twitter: https://twitter.com/CafedeVn
Linkedin: https://www.linkedin.com/in/cafe-dev-407054199/
Pinterest: https://www.pinterest.com/cafedevvn/
YouTube: https://www.youtube.com/channel/UCE7zpY_SlHGEgo67pHxqIoA/
*/
// Classical Java implementation of singleton 
// design pattern
class Singleton
{
    private static Singleton obj;
 
    // private constructor to force use of
    // getInstance() to create Singleton object
    private Singleton() {}
 
    public static Singleton getInstance()
    {
        if (obj==null)
            obj = new Singleton();
        return obj;
    }
}

Ở đây chúng ta đã khai báo getInstance() static để chúng ta có thể gọi nó mà không cần khởi tạo lớp. Lần đầu tiên getInstance() được gọi, nó tạo ra một đối tượng singleton mới và sau đó nó chỉ trả về cùng một đối tượng. Lưu ý rằng Singleton obj không được tạo cho đến khi chúng ta cần nó và gọi phương thức getInstance(). Đây được gọi là khởi tạo lười biếng.

Vấn đề chính của phương pháp trên là nó không an toàn chủ đề. Hãy xem xét trình tự thực hiện sau đây.

Trình tự thực thi này tạo ra hai đối tượng cho singleton. Do đó cách triển khai cổ điển này không an toàn cho chủ đề.

3. Phương pháp 2: Làm cho getInstance() được đồng bộ hóa 

// Thread Synchronized Java implementation of 
// singleton design pattern
class Singleton
{
    private static Singleton obj;
 
    private Singleton() {}
 
    // Only one thread can execute this at a time
    public static synchronized Singleton getInstance()
    {
        if (obj==null)
            obj = new Singleton();
        return obj;
    }
}

Ở đây bằng cách sử dụng đồng bộ hóa đảm bảo rằng chỉ một luồng tại một thời điểm có thể thực thi getInstance(). 

Nhược điểm chính của phương pháp này là sử dụng đồng bộ hóa mọi lúc trong khi tạo đối tượng singleton là tốn kém và có thể làm giảm hiệu suất của chương trình của bạn. Tuy nhiên, nếu hiệu suất của getInstance() không quan trọng đối với ứng dụng của bạn thì phương pháp này cung cấp một giải pháp đơn giản và gọn gàng.

4. Phương pháp 3: Tạo ngay

// Static initializer based Java implementation of
// singleton design pattern
class Singleton
{
    private static Singleton obj = new Singleton();
 
    private Singleton() {}
 
    public static Singleton getInstance()
    {
        return obj;
    }
}

Ở đây chúng ta đã tạo instance của singleton trong bộ khởi tạo tĩnh. JVM thực thi trình khởi tạo tĩnh khi lớp được tải và do đó điều này được đảm bảo là an toàn cho luồng. Chỉ sử dụng phương pháp này khi lớp singleton của bạn nhẹ và được sử dụng trong suốt quá trình thực thi chương trình của bạn.

5. Phương pháp 4 (Tốt nhất): Sử dụng “ Double Checked Lock ” 

Nếu bạn chú ý cẩn thận khi một đối tượng được tạo ra, đồng bộ hóa không còn hữu ích nữa vì bây giờ obj sẽ không còn rỗng và bất kỳ chuỗi hoạt động nào sẽ dẫn đến kết quả nhất quán. 

Vì vậy, chúng ta sẽ chỉ có được khóa trên getInstance() một lần, khi đối tượng là null. Bằng cách này, chúng ta chỉ đồng bộ hóa cách đầu tiên thông qua, chỉ những gì chúng ta muốn. 

// Double Checked Locking based Java implementation of
// singleton design pattern
class Singleton
{
    private static volatile Singleton obj  = null;
 
    private Singleton() {}
 
    public static Singleton getInstance()
    {
        if (obj == null)
        {
            // To make thread safe
            synchronized (Singleton.class)
            {
                // check again as multiple threads
                // can reach above step
                if (obj==null)
                    obj = new Singleton();
            }
        }
        return obj;
    }
}

Chúng ta đã khai báo biến obj dễ dàng đảm bảo rằng nhiều luồng được cung cấp biến obj một cách chính xác khi nó đang được khởi tạo cho instance Singleton. Phương pháp này giảm đáng kể chi phí gọi phương thức được đồng bộ hóa mỗi lần.

Cài ứng dụng cafedev để dễ dàng cập nhật tin và học lập trình mọi lúc mọi nơi tại đây.

Tài liệu từ cafedev:

Nếu bạn thấy hay và hữu ích, bạn có thể tham gia các kênh sau của cafedev để nhận được nhiều hơn nữa:

Chào thân ái và quyết thắng!

Đăng ký kênh youtube để ủng hộ Cafedev nha các bạn, Thanks you!