Làm Chủ Singleton Design Pattern trong 5 Phút với PHP

Singleton Design Pattern Là Gì

Singleton là một phương pháp lập trình trong đó đảm bảo việc khỏi tạo đối tượng từ một class cho trước chỉ diễn ra một lần duy nhất trong toàn bộ quá trình chạy ứng dụng. Do phương pháp lập trình singleton khá phổ biến nên nó còn được coi như một kiểu mẫu trong lập trình hay còn gọi với thuật ngữ tiếng Anh là design pattern.

Tại Sao Cần Singleton

Để dễ hiểu chúng ta sẽ đi vào tìm hiểu một ví dụ cụ thể như sau:

Giả sử bạn đang lập trình một ứng dụng web sử dụng ngôn ngữ lập trình PHP và có sử dụng một database để lưu trữ dữ liệu.

Việc kết nối tới database server được thông qua một class có tên là DatabaseManager:

class DatabaseManager {

}

Bài toán đặt ra ở đây đó là class trên cần được thiết kế làm sao để đảm bảo trong source code của toàn bộ ứng dụng việc khởi tạo object từ class DatabaseManager sẽ trỏ về cùng một đối tượng duy nhất. Điều này đảm bảo trong một lượt truy cập của người dùng, ứng dụng của chúng ta sẽ sử dụng chung một kết nối tới database thay vì tạo nhiều kết nối khác nhau.

Giả sử chúng ta có file Post.php chứa logic lấy dữ liệu của bài viết lưu trong database với ID cho trước như sau:

// Lấy bài viết với ID  cho trước
$dbConnection = new DatabaseManager();
return $dbConnection->getPostContent($postID);

Và một file khác RelatedPosts.php chứa logic lấy dữ liệu của danh sách các bài viết liên quan tới một bài viết cho trước như sau:

// Lấy các bài viết liên quan tới bài viết với ID  cho trước
$dbConnection = new DatabaseManager();
return $dbConnection->getRelatedPosts($postID);

Câu hỏi đặt ra ở đây đó là làm thế nào để cả hai file trên chỉ sử dụng duy nhất một kết nối tới database thay vì hai kết nối khác nhau. Điều này sẽ giảm số lượng kết nối không cần thiết tới database server.

Triển Khai Singleton Design Pattern

Câu trả lời cho bài toán trên đó là sử dụng Singleton vào thiết kết của class DatabaseManager.

class DatabaseManager {
    // Bước 1
    private static $singletonObj = null;

    public static function createSingletonObject() {
        // Bước 3
        if (self::$singletonObj !== null) {
            return self::$singletonObj;
        }

        // Bước 2
        self::$singletonObj = new self();
        return self::$singletonObj;
    }
}

Và lúc này trong đoạn code dưới đây:

$dbConnection_1 = DatabaseManager::createSingletonObject();
$dbConnection_2 = DatabaseManager::createSingletonObject();

Thì 2 biến $dbConnection_1$dbConnection_2 sẽ trỏ về cùng một object được tạo từ class DatabaseManager. Và như vậy chúng ta đã đảm bảo được việc sử dụng chung một kết nối tới database.

Phân tích logic của Singleton trong đoạn code phía trên khác đơn giản:

  • Bước 1: Đầu tiên chúng ta sẽ thêm một thuộc tính tĩnh (static property) cho class DatabaseManager. Thuộc tính này được sử dụng để lưu đối tượng singleton được khởi tạo từ class DatabaseManager.
  • Bước 2: Chúng ta sẽ phân tích đảo ngược thứ tự bước này trước Bước 3. Trong bước này chúng ta khởi tạo một object từ class DatabaseManager sử dụng biểu thức new self(). Đồng thời gán giá trị của đối tượng được khởi tạo vào thuộc tính tĩnh $singletonObj được thêm vào class ở Bước 1 trước đó.
  • Bước 3: Ở bước này chúng ta thêm một câu lệnh điều kiện if để kiểm tra xem giá trị của thuộc tính $singletonObj có khác so với giá trị mặc định null hay không. Nếu khác thì chúng ta biết rằng giá trị này chính là đối tượng đã được khởi tạo trong Bước 2. Việc khởi tạo này có thể được thực hiện ở một thời điểm trước nào đó trong source code của ứng dụng và do đó chúng ta sẽ kết thúc bằng việc trả về đối tượng này. Trong trường hợp ngược lại, nếu như giá trị của thuốc tính $singletonObjnull thì chúng ta biết rằng chưa có đối tượng nào được tạo từ class DatabaseManager thì chúng ta sẽ tạo một đối tượng mới sử dụng từ khoá new như cách làm thông thường.

Điểm mấu chốt trong Singleton mà bạn cần thuộc làm lòng đó là chúng ta sẽ không tạo object từ class thông qua tử khoá new thay vào đó chúng ta sử dụng phương thức tĩnh (static method) có tên là createSingletonObject().

Tuy nhiên với các dự án lớn nhiều người tham gia thì có gì đảm bảo sẽ không có bất cứ ai sử dụng new DatabaseManager trong khi code?

Chặn Việc Tạo Object Sử Dụng new

Một bước quan trọng trong thiết kế các class singleton đó là chúng ta cần ngăn việc tạo một object thông qua sử dụng từ khoá new. Công đoạn này được thực hiện khá đơn giản thông qua thay đổi visibility của phương thức khởi tạo (hay constructor method) của class:

class DatabaseManager {
    private function __construct() {
    }
}

Và lúc này PHP sẽ báo lỗi nếu như có ai đó tạo object từ class DatabaseManager thông qua new DatabaseManager.

Bài Viết Liên Quan