Tìm hiểu về Singleton pattern.
Nội dung bài viết được tham khảo từ cuốn Design pattern for dummies
Ở bài trước, tôi đã giới thiệu cho những bạn về Template pattern: https://viblo.asia/trung.nn.92/posts/ZabG91kkGzY6. Hôm nay tất cả chúng ta sẽ tìm hiểm về Singleton pattern.
Singleton Pattern là một mẫu thiết kế (design pattern) được sử dụng để đảm bảo rằng mỗi một lớp (class) chỉ đã chiếm lĩnh một thể hiện (instance) duy nhất và mọi tương tác đều thông qua thể hiện này.
Singleton Pattern cung cấp một phương thức khởi tạo private, duy trì một tính chất tĩnh để tham chiếu đến một thể hiện của lớp Singleton này. Nó cung cấp thêm một phương thức tĩnh trả về tính chất tĩnh này.
Bài toán thực tế
Bạn gặp một sự cố về hiệu năng mạng lưới hệ thống. Cùng một thời khắc, các bạn đang sử dụng một lúc nhiều đối tượng người tiêu dùng và chúng làm tiêu tốn quá nhiều tài nguyên của mạng lưới hệ thống. Đây là vấn đề mà bạn phải phải khắc phục, và Singleton pattern có thể giúp đỡ bạn thực hiện được điều đó.
Tạo một đối tượng người tiêu dùng duy nhất với mẫu Singleton
Tất cả chúng ta khai mạc với mẫu Singleton và xử lý rối rắm mà tất cả chúng ta vừa gặp phải. Tất cả chúng ta muốn vững chắc rằng chỉ tạo duy nhất 1 đối tượng người tiêu dùng cho một lớp cụ thể dù cho tất cả những người khác có nỗ lực cố gắng tạo bao nhiêu đối tượng người tiêu dùng đi nữa.
Các bạn đang tạo ra hàng trăm đối tượng người tiêu dùng Database trong mã ngu62n, và rối rắm là từng đối tượng người tiêu dùng này rất lớn. Đâu là giải pháp? Mấu duy nhất (Singeton pattern) là lời giải đáp.
Mẫu duy nhất Singleton vững chắc rằng bạn cũng có thể khởi tạo chỉ duy nhất một đối tượng người tiêu dùng cho một lớp. Nếu như bạn không sử dụng mẫu thiết kế này, toán tử new như thường sử dụng, sẽ tạo ra liên tục nhiều đối tượng người tiêu dùng mới như sau:
Bạn sử dụng mẫu Singleton khi chúng ta muốn hạn chế việc sử dụng tài nguyên hoặc khi chúng ta phải xử lý 1 đối tượng người tiêu dùng nhạy cảm mà tài liệu của nó không thể san sẻ cho mọi thể hiện.
Bất luận khi nào bạn thật sự cần duy nhất 1 thể hiện của một lớp, hãy nghĩ tới mẫu Singleton thay vì dùng toán tử new.
Tạo một lớp Database theo phong cách Singleton
Tất cả chúng ta khai mạc vào xây dựng lớp Database:
class Database { private $record; private $name; public function Database($name) { $this->name = $name; $this->record = 0; } … }
Bạn cần phải thêm vào 2 hàm editRecord(), được cho phép bạn chỉnh sửa 1 bản ghi và hàm getName() để lấy ra tên của Database.
class Database { private $record; private $name; public function Database($name) { $this->name = $name; $this->record = 0; } public function getName() { return $this->name; } public function editRecord($operation) { return “Performing a ” . $operation . ” operation on record ” . $this->record . ” in database ” . $this->name; } }
Tới giờ mọi việc vẫn tốt đẹp. Bất luận khi nào bạn tạo 1 đối tượng người tiêu dùng bằng toán tử new, một đối tượng người tiêu dùng mới sẽ tiến hành tạo ra. Nếu như bạn tạo 3 database, các bạn sẽ có 3 đối tượng người tiêu dùng như sau:
$dataOne = new Database(“Product”); $dataTwo = new Database(“Product also”); $dataThree = new Database(“Product again”);
Làm thế nào để bạn cũng có thể tránh việc tạo 1 đối tượng người tiêu dùng khi mới sử dụng toán tử new? Đây là một giải pháp: làm cho hàm khởi tạo trở thành private:
private function Database($name) { $this->name = $name; $this->record = 0; }
Với cách làm này tất cả chúng ta không thể gọi được hàm khởi tạo (contructor) của nó, tức là không thực hiện được dòng lệnh này: $data = new Database(“”). Tuy nhiên nếu không gọi được hàm khởi tạo thì làm thế nào mà ta tạo đối tượng người tiêu dùng được? Vậy làm thế nào bạn cũng có thể tạo 1 đối tượng người tiêu dùng khi chúng ta không thể gọi hàm khởi tạo nó?
Do đó tất cả chúng ta phải xây dựng 1 hàm nào đó để thay thế cho hàm khở tạo, và khi gọi hàm này thì đối tượng người tiêu dùng được phát sinh ra theo một cách đặc biệt quan trọng (đặc biệt quan trọng ở đoạn nếu chưa xuất hiện đối tượng người tiêu dùng thì nó sẽ tạo đối tượng người tiêu dùng mới, còn nếu có rồi thì nó trỏ vào đối tượng người tiêu dùng đã có rồi, như vậy thì đảm bảo lớp học luôn tồn tại 1 đối tượng người tiêu dùng). Hàm này thường được đặt tên là getInsstance(). Lưu ý rằng hàm này phải được khai báo kiểu public và static để bạn cũng có thể truy cập tới nó thông qua tên lớp.
public static function getInstance($name) { }
Hàm này sẽ trả về một đối tượng người tiêu dùng Database, nhưng hàm chỉ hoạt động khi có ít nhất một đối tượng người tiêu dùng đã tồn tại. Vì thế trước nhất ta cần kiểm tra đối tượng người tiêu dùng này, nếu đối tượng người tiêu dùng không được khởi tạo thì ta cần khởi tạo nó.
private static $singletonObject; public static function getInstance($name) { if ($this->singletonObject == null) { $this->singletonObject = new Database($name); } return $this->singletonObject; }
Vấn đề đã được xử lý. Lúc này chỉ có duy nhất 1 đối tượng người tiêu dùng Database tồn tại trong cùng một thời điểm. Việc gọi hàm getInstance() sẽ cho ta 1 đối tượng người tiêu dùng như sau:
Lúc này, không quan tâm đến việc bạn gọi bao nhiêu lần hàm getInstance(), bạn luôn nhận được cùng 1 đối tượng người tiêu dùng. Đó đó chính là cách bạn làm với mẫu Singleton.
Chạy thử ví dụ với mẫu Singleton
Mở màn bằng việc tạo một đối tượng người tiêu dùng Database với tên là products, sau đó gọi hàm getName():
$database = Database::getInstance(“Product”); echo “This is the ” . $database->getname() . ” database.”;
Sau đó bạn tiếp tục tạo 1 đối tượng người tiêu dùng Database với tên là employees, và gọi lại hàm getName() để kiểm tra:
$database = Database::getInstance(“Employees”); echo “This is the ” . $database->getname() . ” database.”;
Tuy nhiên, đối tượng người tiêu dùng Database đã được tạo, vì vậy trong lần thứ hai, hàm getInstance () vẫn trả về đối tượng người tiêu dùng Database cũ.
Vậy là bạn đã nhận được được duy nhất 1 đối tượng người tiêu dùng cho dù đã thực hiện việc tạo gấp hai. Phương pháp bạn làm vệc như sau: ngăn cản việc khởi tạo bằng toán tử new, và tạo 1 hàm để tạo đối tượng người tiêu dùng theo ý bạn. Đó đó chính là cách hoạt động của mẫu Singleton
Vậy là tất cả chúng ta vừa tìm hiểu xong về Singleton Pattern. Hy vọng nội dung bài viết sẽ giúp ích cho những bạn trong việc lập trình.