development:software-architecture:design-patterns:singleton
Singleton
Để kiểm soát quyền truy cập đến một số tài nguyên được chia sẻ như Database/File mà không muốn đóng mở kết nối rải rác ở nhiều nơi trên ứng dụng mỗi lần sử dụng.
Chúng ta có thể sử dụng biến global để lưu trữ một số đối tượng cần thiết. Tuy nhiên cách này có rủi ro, không an toàn, dễ gây lỗi và khó kiểm soát do bất kỳ đoạn mã nào trên ứng dụng cũng có thể ghi đè nội dung lên biến đó.
Mẫu Singleton giúp tạo một đối tượng cho phép truy cập được ở bất kỳ đâu trên ứng dụng mà không cần phải khởi tạo lại ra một đối tượng mới và đảm bảo không bị ghi đè bởi các đoạn mã khác.
class Test { static private $_instance = NULL; // Private methods cannot be called private function __construct() {} static function getInstance() { if (self::$_instance == NULL) { self::$_instance = new SomeClass(); } return self::$_instance; } public function test() { //... } } $data = Test::getInstance()->test();
- Singleton.php
/** * If you need to support several types of Singletons in your app, you can * define the basic features of the Singleton in a base class, while moving the * actual business logic (like logging) to subclasses. */ class Singleton { /** * The actual singleton's instance almost always resides inside a static * field. In this case, the static field is an array, where each subclass of * the Singleton stores its own instance. */ private static $instances = []; /** * Singleton's constructor should not be public. However, it can't be * private either if we want to allow subclassing. */ protected function __construct() { } /** * Cloning and unserialization are not permitted for singletons. */ protected function __clone() { } public function __wakeup() { throw new \Exception("Cannot unserialize singleton"); } /** * The method you use to get the Singleton's instance. */ public static function getInstance() { $subclass = static::class; if (!isset(self::$instances[$subclass])) { // Note that here we use the "static" keyword instead of the actual // class name. In this context, the "static" keyword means "the name // of the current class". That detail is important because when the // method is called on the subclass, we want an instance of that // subclass to be created here. self::$instances[$subclass] = new static; } return self::$instances[$subclass]; } }
- Logger.php
/** * The logging class is the most known and praised use of the Singleton pattern. * In most cases, you need a single logging object that writes to a single log * file (control over shared resource). You also need a convenient way to access * that instance from any context of your app (global access point). */ class Logger extends Singleton { /** * A file pointer resource of the log file. */ private $fileHandle; /** * Since the Singleton's constructor is called only once, just a single file * resource is opened at all times. * * Note, for the sake of simplicity, we open the console stream instead of * the actual file here. */ protected function __construct() { $this->fileHandle = fopen('php://stdout', 'w'); } /** * Write a log entry to the opened file resource. */ public function writeLog(string $message): void { $date = date('Y-m-d'); fwrite($this->fileHandle, "$date: $message\n"); } /** * Just a handy shortcut to reduce the amount of code needed to log messages * from the client code. */ public static function log(string $message): void { $logger = static::getInstance(); $logger->writeLog($message); } }
- Config.php
/** * Applying the Singleton pattern to the configuration storage is also a common * practice. Often you need to access application configurations from a lot of * different places of the program. Singleton gives you that comfort. */ class Config extends Singleton { private $hashmap = []; public function getValue(string $key): string { return $this->hashmap[$key]; } public function setValue(string $key, string $value): void { $this->hashmap[$key] = $value; } }
- Singleton.php
<?php namespace OneSite\DesignPattern\Tests; use OneSite\DesignPattern\Singleton\Config; use OneSite\DesignPattern\Singleton\Logger; class SingletonTest { public function testSingletonPattern() { /** * The client code. */ Logger::log("Started!"); // Compare values of Logger singleton. $l1 = Logger::getInstance(); $l2 = Logger::getInstance(); if ($l1 === $l2) { Logger::log("Logger has a single instance."); } else { Logger::log("Loggers are different."); } // Check how Config singleton saves data... $config1 = Config::getInstance(); $login = "test_login"; $password = "test_password"; $config1->setValue("login", $login); $config1->setValue("password", $password); // ...and restores it. $config2 = Config::getInstance(); if ($login == $config2->getValue("login") && $password == $config2->getValue("password") ) { Logger::log("Config singleton also works fine."); } Logger::log("Finished!"); return $this->assertTrue(true); } }
development/software-architecture/design-patterns/singleton.txt · Last modified: 2024/08/21 05:33 by tungnt