Я не хочу создавать дискуссию об Singleton лучше, чем статическую или лучше глобальной, и т. Д. Я читал десятки вопросов о подобных предметах на SO, но я не мог ответить на этот КОНКРЕТНЫЙ вопрос, поэтому я надеюсь теперь кто-то может осветить меня, ответив на этот вопрос одним (или более) реальными простыми примерами , а не только на теоретические дискуссии.
В моем приложении у меня есть типичный класс DB для абстрагирования уровня БД и выполнения задач в БД без необходимости писать везде в коде mysql_connect / mysql_select_db / mysql...
Я мог бы написать класс как STATIC CLASS:
class DB { private static $connection = FALSE; //connection to be opened //DB connection values private static $server = NULL; private static $usr = NULL; private static $psw = NULL; private static $name = NULL; public static function init($db_server, $db_usr, $db_psw, $db_name) { //simply stores connections values, without opening connection } public static function query($query_string) { //performs query over alerady opened connection, if not open, it opens connection 1st } ... }
ИЛИ как СИНГЛЕТОН:
class DBSingleton { private $inst = NULL; private $connection = FALSE; //connection to be opened //DB connection values private $server = NULL; private $usr = NULL; private $psw = NULL; private $name = NULL; public static function getInstance($db_server, $db_usr, $db_psw, $db_name) { //simply stores connections values, without opening connection if($inst === NULL) $this->inst = new DBSingleton(); return $this->inst; } private __construct()... public function query($query_string) { //performs query over already opened connection, if connection is not open, it opens connection 1st } ... }
Тогда после того, как в моем приложении, если я хочу запросить БД, я мог бы сделать
//Performing query using static DB object DB:init(HOST, USR, PSW, DB_NAME); DB::query("SELECT..."); //Performing query using DB singleton $temp = DBSingleton::getInstance(HOST, USR, PSW, DB_NAME); $temp->query("SELECT...");
Для меня Синглтон получил единственное преимущество, чтобы избежать объявления как static
каждого метода класса. Я уверен, что некоторые из вас могут дать мне ПРИМЕР реального преимущества синглтона в этом конкретном случае . Заранее спасибо.
Что не так со следующим (упрощенным) примером:
class Database { protected $_connection; protected $_config; public function __construct( array $config ) // or other means of passing config vars { $this->_config = $config; } public function query( $query ) { // use lazy loading getter return $this->_getConnection()->query( $query ); } protected function _getConnection() { // lazy load connection if( $this->_connection === null ) { $dsn = /* create valid dsn string from $this->_config */; try { $this->_connection = new PDO( $dsn, $this->_config[ 'username' ], $this->_config[ 'password' ] ); } catch( PDOException $e ) { /* handle failed connecting */ } } return $this->_connection; } } $db1 = new Database( array( 'driver' => 'mysql', 'host' => 'localhost', 'dbname' => 'test', 'username' => 'test_root', 'password' => '**********' ) ); $db2 = new Database( array( 'driver' => 'pgsql', 'host' => '213.222.1.43', 'dbname' => 'otherdb', 'username' => 'otherdb_root', 'password' => '**********' ) ); $someModel = new SomeModel( $db1 ); $someOtherModel = new SomeOtherModel( $db2 ); $yetAnotherModel = new YetAnotherModel( $db2 );
Это демонстрирует, как вы можете использовать ленивые загрузочные соединения и по-прежнему иметь гибкость при использовании разных подключений к базе данных.
Экземпляры базы данных будут подключаться только к их индивидуальному соединению, когда объект, который потребляет один из экземпляров (в данном случае одну из моделей), решает вызвать метод экземпляра.
В моем последнем проекте я фактически пошел против «хороших» принципов проектирования, сделав класс базы данных полностью статичным. Причина этого в том, что я использовал много кеширования на объектах PHP. Первоначально у меня была база данных, переданная через конструктор каждого объекта в качестве инъекции зависимости, однако я хотел убедиться, что база данных не должна соединяться, если это абсолютно необходимо. Таким образом, использование базы данных в качестве переменной-члена этого объекта было бы нецелесообразным, потому что если вы не инициализировали объект из кеша, вы не захотите подключаться к базе данных, если вы на самом деле не выполнили операцию над ним.
Поэтому в итоге у меня было только две (общедоступные) статические функции, Database :: fetch () и Database :: execute (), которые проверяли бы, было ли оно уже подключено, а если нет, оно будет подключаться и выполнять запрос. Таким образом, мне не пришлось бы беспокоиться о десериализации и подключаться как можно реже. Это технически делает модульное тестирование невозможным, хотя.
Вы не всегда должны следовать каждой хорошей практике. Но я по-прежнему рекомендую не делать то, что я сделал, поскольку некоторые считают это преждевременной оптимизацией.
Мой совет: STOP, используя Singleton и статические все вместе.
Зачем? Потому что вы будете вставлять зависимости, которые сделают ваш код непригодным для использования в других проектах, и не позволят его тестировать. Также забудьте о свободном соединении, если используете синглтон.
Альтернативы? Внедрение зависимости. http://www.potstuck.com/2009/01/08/php-dependency-injection
Создание библиотеки библиотек БД, конечно, короче и быстрее, чем выполнение:
$ db = DBSingleton :: blabla (); // каждый раз, когда мне нужно
Но также, поскольку это глобально, заманчиво использовать всюду.
Итак, выберите другие методы, если вы хотите чистый код … и выберите статический, если вам нужен быстрый код 😉
/* Data base*/ class Database { /* Database field definition */ private static $_instance; /instance private $_connection; private $DB_USER = "database_user_name_here"; private $DB_PASS = "your_password_here"; private $DB_NAME = "your_database_name_here"; private $DB_SERVER = "localhost"; /* Initiate the database connection */ private function __construct() { $this->_connection = new mysqli($this->DB_SERVER , $this->DB_USER , $this->DB_PASS , $this->DB_NAME); /* Test if connection succeeded */ if (mysqli_connect_errno()) { die("Database connection failed: " . mysqli_connect_error() . " (" . mysqli_connect_errno() . ")" ); } } /** * Instance of the database * @return Database * */ public static function Instance() { if (!self::$_instance) { // If no instance then make one self::$_instance = new self(); } return self::$_instance; } /** * Void duplicate connection */ private function __clone() { } /* Return a connection */ public function getConnection() { return $this->_connection; } } /** This is how you would use it in a different class. @var TYPE_NAME $connection */ $db = Database::Instance(); $connection = $db->getConnection();