Программистам php, приходится мириться с тем, что переменная объявленная в любом месте скрипта, не видна ни в одной из функций или методов.
Конечно, есть ключевое слово global. Но вписывать туда все используемые переменные очень проблематично, и захламляет код программы.
Поясню на примере:
Пусть есть класс db - обертка над стандартным mysql_query. И класс read_books, который может увеличивать количество просмотров определенной кники по его id.
В большинстве случаев используют такую конструкцию:
1 2 3 4 5 6 7 8 9 10 11 12 | <?php class read_books{ function __construct(){ $this ->db = new db(); $this ->db->connect(); } function f( $id ){ $this ->db->query( 'update books set hits=hits+1 where id=' . $id ); } } $rd = new read_books(); $rd ->f(122); |
Из недостатков: для каждого экземпляра read_books будет создаваться отдельное подключение к бд. Понятно, что для работы скрипта, достаточно лишь одного подключения. Поэтому, многие пишут так:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | <?php $db = new db(); $db ->connect(); class read_books{ function __construct(){ global $db ; $this ->db = $db ; } function f( $id ){ $this ->db->query( 'update books set hits=hits+1 where id=' . $id ); } function f1( $id ){ // либо используем $db напрямую global $db ; $db ->query( 'update books set hits=hits+1 where id=' . $id ); } } $rd = new read_books(); $rd ->f(122); $rd ->f1(123); |
Минусы я озвучил выше. По мере разрастания программы, следить за доступностью такой глобальной переменной, станет все сложнее и сложнее, а иногда и просто невозможно.
В этой статье я расскажу про технику Singleton'ов, с которой я познакомился благодаря Хабру.
Итак, шаблон проектирования Singleton, это такая структура классов, по которой у одного класса может быть только один экземпляр. Это оправданно для большинства служебных классов: работа с БД, работы с шаблонизатором, роутер и т.д.
При этом, сам экземпляр класса не создается явно через ключевое слово new, а доступен прямо из объявления класса, т.е. глобально. Непонятно? Сейчас покажу, как это на самом деле просто.
<?php abstract class Singleton{ // в $_aInstances будут хранится все // экзмепляры всех классов наследующих класс Singleton private static $_aInstances = array (); public static function getInstance() { $sClassName = get_called_class(); // название класса экземпляр которого мы запросили if ( class_exists ( $sClassName ) ){ if ( !isset( self:: $_aInstances [ $sClassName ] ) ) // если экземпляр класса еще не был создан, создаем его self:: $_aInstances [ $sClassName ] = new $sClassName (); // возвращаем один экземпляр return self:: $_aInstances [ $sClassName ]; } return 0; } // более удобный вызов метода getInstance public static function gI() { return self::getInstance(); } // так как нам нужен лишь один экземпляр любого класса, // то копировать объекты нам не потребуется final private function __clone(){} private function __construct(){} } |
теперь при создании класса наследуем Singleton
<?php class db extends Singleton{ private $connid = 0; function query( $sql ){ // запрос return mysql_query(Sql, $this ->connid); } function connect(){ // подключение $this ->connid = @mysql_connect( '127.0.0.1' , 'root' , '' ) ; mysql_select_db( 'books' , $this ->connid); } } db:gi()->connet(); // правда удобно?! |
и в нашем классе тоже доступен db:gi()
<?php class read_books{ function f( $id ){ db::gi()->query( 'update books set hits=hits+1 where id=' . $id ); } } $rd = new read_books(); $rd ->f(122); |
И никаких проблем с видимостью. Для всех read_books будет создан всего один экземпляр db с одним подключением. В купе c техникой автозагрузки необходимых файлов на php заботиться о том, чтобы класс был подключен а переменная была видна больше нет необходимости.
Комментарии
Вуаля. Если пишите поучительное что то... убедитесь что работает.