07/09/2018, 17:57

PHP - Design Pattern: Singleton

Singleton là pattern đảm bảo việc một ứng dụng trong một thời điểm chỉ được phép có duy nhất một thực thể của đối tượng Singleton. Có nghĩa là việc khởi tạo đối tượng chỉ duy nhất một lần đầu tiên, các lần sau, nó không được khởi tạo mới mà chính là đối tượng cũ. Điều này giúp tiết kiệm bộ nhớ và ...

Singleton là pattern đảm bảo việc một ứng dụng trong một thời điểm chỉ được phép có duy nhất một thực thể của đối tượng Singleton. Có nghĩa là việc khởi tạo đối tượng chỉ duy nhất một lần đầu tiên, các lần sau, nó không được khởi tạo mới mà chính là đối tượng cũ. Điều này giúp tiết kiệm bộ nhớ và ngăn chặn việc tạo ra nhiều lần khởi tạo đối tượng. Trong bài này mình sẽ giới thiệu về Singleton trong PHP

Khi trong ứng dụng có những thực thể bị truy xuất nhiều lần nhưng không cần thiết phải khởi tạo lại. Ví dụ:

  • Database connect: nếu không dùng Singleton, mỗi lần muốn thao tác với Database, chúng ta cần phải khởi tạo lại lớp Database.
  • Logger: trong ứng dụng, có nhiều chỗ cần phải lưu log, hãy tưởng tượng nếu không dùng Singleton, mỗi lần muốn lưu log lại phải khởi tạo lại lớp Logger.
  • Đọc config file: bạn có một file chứa nhiều thông số config. Nếu không dùng Singleton, mỗi lần muốn đọc một thông số nào đó, bạn lại phải khởi tạo lại.

Để xây dựng Singleton, bạn cần đáp ứng được các yêu cầu sau:

  • Define thuộc tính (biến) private static instance
  • Define public static function getInstance() trả về instance theo dạng lazy initialization (khởi tạo trong lần đầu).
  • Define __construct() thành protected hoặc private để ngăn người dùng khởi tạo trực tiếp.
class Singleton
{
    private static $instance;

    private function __construct()
    {
    }

    public static function getInstance()
    {
        if (null === static::$instance) {
            static::$instance = new static;
        }
        return static::$instance;
    }
}

Để 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 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:

<?php

class DatabaseManager
{
    private static $instance = null;
    private $conn;

    private $host = 'localhost';
    private $user = 'root';
    private $pass = 'root';
    private $name = 'test';

    private function __construct()
    {
        $this->conn = new PDO("mysql:host={$this->host};
    dbname={$this->name}", $this->user, $this->pass,
            array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'"));
    }

    public static function getInstance()
    {
        if (!self::$instance) {
            self::$instance = new DatabaseManager();
        }

        return self::$instance;
    }

    public function getConnection()
    {
        return $this->conn;
    }
}

Vì chúng ta sử dụng một lớp kiểm tra nếu một kết nối đã tồn tại trước khi nó thiết lập một kết nối mới, nó thực sự không quan trọng bao nhiêu lần chúng ta tạo một đối tượng mới ra khỏi lớp, chúng ta vẫn nhận được kết nối tương tự.

$instance = DatabaseManager::getInstance();
$conn = $instance->getConnection();
var_dump($conn);

$instance = DatabaseManager::getInstance();
$conn = $instance->getConnection();
var_dump($conn);

$instance = DatabaseManager::getInstance();
$conn = $instance->getConnection();
var_dump($conn);

Để hiểu được vấn đề mà mẫu singleton giải quyết, hãy xem xét lớp sau không có cơ chế để kiểm tra xem kết nối đã tồn tại chưa trước khi nó thiết lập kết nối mới.

<?php

class ConnectDbWOSingleton
{
    private $conn;

    private $host = 'localhost';
    private $user = 'root';
    private $pass = 'root';
    private $name = 'test';

    public function __construct()
    {
        $this->conn = new PDO("mysql:host={$this->host};
    dbname={$this->name}", $this->user, $this->pass,
            array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES 'utf8'"));
    }

    public function getConnection()
    {
        return $this->conn;
    }
}

Bây giờ, mỗi khi chúng ta tạo một đối tượng mới, chúng ta cũng thiết lập một kết nối cơ sở dữ liệu mới.

$instance = new ConnectDbWOSingleton();
$conn = $instance->getConnection();
var_dump($conn);
 
$instance = new ConnectDbWOSingleton();
$conn = $instance->getConnection();
var_dump($conn);
 
$instance = new ConnectDbWOSingleton();
$conn = $instance->getConnection();
var_dump($conn);

Singleton pattern là design pattern tương đối đơn giản, nhưng việc áp dụng khá hiệu quả, nó tạo ra một thực thể duy nhất để sử dụng, giúp tối ưu hóa việc sử dụng tài nguyên để chạy ứng dụng. Singleton pattern cũng thường được so sánh với Factory pattern, nhưng mỗi mẫu lập trình có những điểm mạnh yếu khác nhau.

0