Применение паттерна Singleton. Классы для работы с БД

Здравствуйте дорогие читатели.
В последнее время большинство своих работ и проектов я пишу с применением ООП.
В таких приложениях часто возникает потребность в неких глобальных объектах к примеру (база данных,настройки и т.д) которые должны быть доступны повсюду.

Существует несколько вариантов решения проблемы.
1. Глобальные переменные
2. Использование паттерна Singleton

1) Применение глобальных переменных всегда довольно соблазнительно ,действительно что может быть проще объявил один раз на протяжении сотен строк кода одну переменную как глобальную, присвоил ей значение да и пользуйся на свое здоровье =)
Однако здесь ситуацию сродни как с алкоголем ,когда поднимают тосты за здоровье ,и при этом травясь им.
Минусы очевидны — это невозможность быть уверенным в поведении подобных переменных часто они слишком «непредсказуемы».
Глобальную переменную легко затереть ,изменить ее данные ,а в ряде случаев это приводит к большим проблемам в выявлении ошибок и багов приложения.
2) Использование паттерна Singleton
Паттерн Singleton — один из наиболее известных шаблонов проектирования в программировании.
Он дает нам возможность как в случае с глобальными переменными создать глобальный объект ,но при этом мы будем уверены в его поведении ,т.к объект создается единственный раз и изменить его содержимое нельзя.

* Следует заметить что применять паттерн Singleton следует редко да метко. Не стоит забывать об одном из самых главных принципов объектно-ориентированного программирования — инкапсуляции.

От слов к делу =)

class DB {
		private static $_instance=null;
		private function __construct() {}
		private function __clone() {}
		public static function run() {
	 		if (self::$instance == NULL) {
                        self::$instance = new Class_name();
                        }
                return self::$instance;
		}
		final public function __destruct() {
			self::$_instance = null;
		}
}

В данном случае мы создали класс синглетон для доступа к базе.
Переменная $_instance статическая что позволяет хранить состояние объекта на протяжении всей его работы.
После того как мы единожды создали объект мы будем работать только с этим объектом ,независимо от количество вызовов.
Теперь для доступа к объекту нужно будет вызвать метод run()

$db=DB::run();

Напоследок приведу пару классов для работы с БД посредством интерфейса PDO. С некоторым логгированием количество запросов к БД,времени генерации и т.д

/**
 * Классы работы с базой
*/
class DB {
		private static $_instance=null;
		private function __construct() {}
		private function __clone() {}
		public static function run() {
			if (!isset(self::$_instance)) {
				try {
					self::$_instance = new DBH('mysql:host='.DB_HOST.';dbname='.DB_BASE, DB_USER, DB_PASS,array(PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES utf8"));
				} catch (PDOException $e) {
					throw new Exception('Ошибка соединения с базой данных '.$e->getMessage());
				}
			}
			return self::$_instance;
		}
		final public function __destruct() {
			self::$_instance = null;
		}
}
class DBH extends PDO {
		protected
			$query_count = 0,
			$exec_time = 0;
		private
			$logger_callback = NULL;
		public function __construct($dsn, $username = null, $password = null, $driver_options = array(), $logger_callback = NULL)
		{
			parent::__construct($dsn, $username, $password, $driver_options);
			$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
			if (!$this->getAttribute(PDO::ATTR_PERSISTENT))
			{
				$this->setAttribute(PDO::ATTR_STATEMENT_CLASS, array('DBPDOStatement', array($this)));
			}
			$this->logger_callback = $logger_callback;
		}
		public function increment_query_count()
		{
			$this->query_count++;
		}
		public function get_query_count()
		{
			return $this->query_count;
		}
		public function add_exec_time($time)
		{
			$this->exec_time += $time;
		}
		public function get_exec_time_ms()
		{
			return $this->exec_time;
		}
		public function log()
		{
			if (!is_null($this->logger_callback))
			{
				$args = func_get_args();
				call_user_func_array($this->logger_callback, $args);
			}
		}
		public function exec($sql)
		{
			$this->log($sql, 'Query (PDO->exec())');
			$this->increment_query_count();
			$start = microtime(true);
			$return = parent::exec($sql);
			$finish = microtime(true);
			$this->add_exec_time($finish-$start);
			return $return;
		}
		public function query()
		{
			$this->increment_query_count();
			$args = func_get_args();
			$this->log($args, 'Query (PDO->query())');
			$start = microtime(true);
			$return = call_user_func_array(array($this, 'parent::query'), $args);
			$finish = microtime(true);
			$this->add_exec_time($finish-$start);
			return $return;
		}
}
class DBPDOStatement extends PDOStatement {
		protected
			$db;
		private
			$params = array();
		protected static $type_map = array(
			PDO::PARAM_BOOL => "PDO::PARAM_BOOL",
			PDO::PARAM_INT => "PDO::PARAM_INT",
			PDO::PARAM_STR => "PDO::PARAM_STR",
			PDO::PARAM_LOB => "PDO::PARAM_LOB",
			PDO::PARAM_NULL => "PDO::PARAM_NULL"
		);
		protected function __construct(DBH $db)
		{
			$this->pdo = $db;
		}
		public function execute($input_parameters = null)
		{
			$this->pdo->log($this->queryString, 'Query (PDOStatement->execute())');
			if (!empty($this->params))
			{
				$this->pdo->log($this->params, 'Parameters');
			}
			if (!empty($input_parameters))
			{
				$this->pdo->log($input_parameters, 'Parameters');
			}
			$this->pdo->increment_query_count();
			$start = microtime(true);
			$return = parent::execute($input_parameters);
			$finish = microtime(true);
			$this->pdo->add_exec_time($finish-$start);
			return $return;
		}
		public function bindValue($pos, $value, $type = PDO::PARAM_STR)
		{
			$type_name = isset(self::$type_map[$type]) ? self::$type_map[$type] : '(default)';
			$this->params[] = array($pos, $value, $type_name);
			$return	= parent::bindValue($pos, $value, $type);
			return $return;
		}
}

Использование аналогичное

$db=DB::run();
$db->query();
// Или же вызову напрямую в каждом отдельном запросе
DB::run()->query();
// Вывод кол.запросов к базе
echo DB::run() ->get_query_count();
// Показ времени генерации запросов
echo round(DB::run() ->get_exec_time_ms(),4).' с';

Вам также может понравиться ...