Menu
CoddyTech

Mẫu Singleton

Thuộc phần Lập trình hướng đối tượng trong hành trình PHP của Coddy. Bài học 71 trên 91.

Mẫu Singleton (Singleton Pattern) là một mẫu thiết kế khởi tạo, đảm bảo một lớp chỉ có một instance trong suốt vòng đời ứng dụng. Mẫu này cũng cung cấp một điểm truy cập toàn cục đến instance đó.

Mẫu này hữu ích khi bạn cần chính xác một đối tượng điều phối các hoạt động trên toàn hệ thống, chẳng hạn như kết nối cơ sở dữ liệu, trình quản lý cấu hình hoặc trình ghi nhật ký. Tạo nhiều đối tượng như vậy sẽ gây lãng phí tài nguyên hoặc dẫn đến hành vi không nhất quán.

Sau đây là cách triển khai Singleton trong PHP:

<?php
class Database {
    private static ?Database $instance = null;
    
    private function __construct() {
        // Hàm khởi tạo (constructor) private ngăn việc tạo instance trực tiếp
    }
    
    public static function getInstance(): Database {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
    
    public function query(string $sql): string {
        return "Executing: $sql";
    }
}

$db1 = Database::getInstance();
$db2 = Database::getInstance();

echo $db1 === $db2 ? "Same instance" : "Different instances";

Đầu ra:

Same instance

Mẫu này dựa trên ba thành phần chính: hàm khởi tạo riêng tư để ngăn việc gọi trực tiếp new, thuộc tính tĩnh lưu giữ một instance duy nhất và phương thức tĩnh tạo instance ở lần gọi đầu tiên rồi trả về instance đó trong những lần gọi tiếp theo.

Để Singleton thực sự không thể bị phá vỡ, bạn cũng nên ngăn việc sao chép và giải tuần tự hóa:

<?php
private function __clone() {}
public function __wakeup() {
    throw new Exception("Cannot unserialize singleton");
}
challenge icon

Thử thách

Dễ

Hãy xây dựng một trình quản lý cấu hình bằng mẫu Singleton (Singleton Pattern). Trong các ứng dụng thực tế, bạn thường muốn có một nguồn dữ liệu chuẩn duy nhất cho các thiết lập cấu hình: nếu có nhiều đối tượng cấu hình, hệ thống của bạn có thể hoạt động không nhất quán.

Bạn sẽ sắp xếp code trong hai tệp:

  • Config.php: Tạo một lớp Config triển khai mẫu Singleton. Trình quản lý cấu hình cần:
    • Lưu các thiết lập trong một thuộc tính mảng riêng tư
    • Có hàm khởi tạo riêng tư, khởi tạo mảng thiết lập ở trạng thái rỗng
    • Cung cấp phương thức tĩnh getInstance() để trả về instance duy nhất
    • Ngăn việc sao chép bằng cách đặt __clone() ở chế độ riêng tư
    • Có phương thức set(string $key, string $value): void để lưu một thiết lập
    • Có phương thức get(string $key): ?string để trả về giá trị của một khóa, hoặc null nếu khóa đó không tồn tại
    • Có phương thức getAll(): array để trả về tất cả thiết lập
  • main.php: Thêm tệp Config. Bạn sẽ nhận hai đầu vào: một khóa và một giá trị.

    Lấy instance Config và đặt một thiết lập mặc định với khóa "app_name" và giá trị "MyApp".

    Lấy tham chiếu thứ hai đến instance Config (gọi lại getInstance()) rồi dùng tham chiếu đó để đặt một thiết lập khác bằng khóa và giá trị từ đầu vào.

    Cuối cùng, hãy xác minh Singleton hoạt động chính xác:

    • In ra "Same instance: true" hoặc "Same instance: false" tùy thuộc vào việc cả hai tham chiếu có trỏ đến cùng một đối tượng hay không (dùng === để so sánh)
    • In ra "Settings count: [count]" để hiển thị tổng số thiết lập đã lưu (dùng count() trên kết quả của getAll())
    • In ra "App name: [value]" để hiển thị thiết lập app_name được lấy thông qua tham chiếu thứ hai

Thử thách này minh họa lợi ích cốt lõi của mẫu Singleton: dù bạn gọi getInstance() bao nhiêu lần, bạn vẫn luôn nhận được cùng một đối tượng, nhờ đó mọi phần trong ứng dụng đều dùng chung trạng thái cấu hình.

Tự làm thử

<?php

require_once 'Config.php';

// Đọc đầu vào
$key = trim(fgets(STDIN));
$value = trim(fgets(STDIN));

// TODO: Lấy instance của Config và đặt "app_name" thành "MyApp"

// TODO: Gọi lại getInstance() để lấy tham chiếu thứ hai đến Config

// TODO: Dùng tham chiếu thứ hai để đặt giá trị cài đặt bằng $key và $value từ đầu vào

// TODO: Kiểm tra Singleton có hoạt động không:
// - In ra "Same instance: true" hoặc "Same instance: false" dựa trên phép so sánh ===
// - In ra "Settings count: [count]" để hiển thị tổng số cài đặt
// - In ra "App name: [value]" để hiển thị app_name lấy qua tham chiếu thứ hai

?>
quiz iconTự kiểm tra

Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.

Tất cả bài học trong Lập trình hướng đối tượng

Tự luyện tập: Trình biên dịch PHP online