Singleton в PHP на примере подключения к MySQL. Шаблон проектирования "Одиночка"(Pattern Singleton) Singleton php примеры

Паттерн Singleton (одиночка) многие ругают, зачастую, называя его анти паттерном. Тем не менее, он достаточно популярен и я пока не встречал крупных программных систем, где он не был бы реализован. Прежде всего, можно отметить фреймворки, где Singleton нередко выступает основой приложения. Также его часто наследуют компоненты, реализующие взаимодействие с конфигурационными данными или механизмом событий, например.

Основной проблемой данного паттерна является подкупающая простота его реализации и использования. Разработчики, не имеющие достаточного количества опыта, применяют Singleton там, где он совершенно не нужен, тем самым лишая программную систему гибкости и пригодности к адекватному тестированию.

Для того чтобы описать шаблон проектирования, сначала необходимо описать задачу, которую он будет решать.

Предположим, что в вашем приложении есть объект, в котором хранятся данные конфигурации приложения. Разумеется, данные конфигурации должны быть доступны разным частям приложения на всех уровнях.

Согласно идеологии ООП, для получения доступа к переменной, например, внутри метода объекта, ее необходимо передать в качестве параметра этого метода или конструктора. Необходимо стараться следовать этому принципу всегда, когда это возможно. Но, в описанной ситуации, трудно представить, через какое количество объектов придется протащить экземпляр класса работы с конфигом.
В PHP есть два способа поместить данные в глобальную область видимости, тем самым, сделать их доступными из любого места программы. Первый – использование суперглобального ассоциативного массива $GLOBALS , содержащего все переменные, объявленные в глобальной области видимости. Второй – использование ключевого слова global , вводящего переменную в текущую область видимости. Какой способ хуже, я затрудняюсь даже предположить. Как бы там ни было, в других языках программирование подобные средства отсутствуют и паттерн Singleton становится единственным способом введения объекта в глобальное пространство.

Класс, реализующий паттерн Singleton становится доступным глобально за счет статического интерфейса. Также к числу его особенностей необходимо отнести блокирование конструктора класса и магических методов __clone() и __wakeup() , описывая их с модификатором доступа private . Это делается для того, чтобы не допустить создание более одного объекта от класса.

newPropetry = "string"; } public static function staticFoo() { return self::getInstance(); } private function __wakeup() { } private function __construct() { } private function __clone() { } } Singleton::getInstance()->Foo(); var_dump(Singleton::staticFoo()); ?>

Единственное свойство класса Singleton::$_instance хранит ссылку на экземпляр, который создается только при первом вызове статического метода Singleton::getInstance() . От повторного создания объекта уберегает условный оператор, с проверкой значения свойства и если ссылка на экземпляр уже существует, она будет возвращена.

Свойство Singleton::$_instance объявлено с модификатором protected , дабы класс можно было наследовать. Нередко паттерн реализуют с сокрытием этого свойства за модификатором private.

Реализацию шаблона Одиночка можно посмотреть, например, в классе CI_Controller из фреймворка CodeIgniter или Zend_Controller_Front из ZendFramework.

Class Singleton { private static $PrS="init private"; public static $PuS="init public"; public static function PrS($A=false) { if($A!==false) self::$PrS=$A; else return self::$PrS; } public static function PuS($A=false) { if($A!==false) self::$PuS=$A; else return self::$PuS; } } echo Singleton::PrS(); echo "\n"; echo Singleton::PuS(); // выведет init private // init public echo "\n -- \n"; $D = new Singleton(); echo $D->PrS(); // также выведет init private echo "\n"; // init public echo $D->PuS(); echo "\n -- \n SET them all!"; // А вот здесь Singleton::PrS("changed private"); // меняем переменные класса Singleton::PuS("changed public"); // используя статическую ссылку echo "\n"; // и попробуем проверить их из "созданного" класса (хотя это просто ссылка копия) echo $D->PrS(); // разумеется, выведет: changed private echo "\n"; echo $D->PuS(); // changed public

скопируйте этот простой пример, запустите его, и вам будет понятно, что статический класс, вместе с его статическими же переменными не дублируется оператором new. Да хоть десять дублей наделайте - все равно - статические переменные останутся.

Слово static для функции говорит о том, что в глобальной таблице при компиляции ей уже выделен адрес - жестко выделен. Так же и со статическими переменными - их адрес также статичен. А НЕстатические переменные (классы) не существуют в адресном пространстве, пока их не определят (оператором new). Обращаться некуда. Для статических адрес уже есть - и к нему (к переменной) можно обратиться всегда - someStaticClass::value

Хотите использовать статический класс для работы с БД - заведите внутри приватную статическую переменную DB_handler. Надо работать с несколькими соединениями (несколько БД) - заведите еще по необходимости. Можно даже соорудить нечто статического массива. Почему нет? Появится необходимость слишком извернуться - перепишите класс. Т.е. копии статических классов вообще не различаются (при изготовлении их оператором new), пока в них не появится хотя бы одна НЕстатическая переменная. Правда, и после этого различаться они будут только этой НЕстатической переменной. Правда, при этом, управлять этой переменной уже получится уже только из изготовленного класса.

Public static function getInstance() { if (is_null(self::$instance)) { self::$instance = new Singleton; } return self::$instance; }

Вот этот кусок кода как раз и возвращает копию ссылки на адрес статического класса Singleton. Это то же самое, что написать Singleton::(и там что-то)

Вот об этом и был вопрос - "ЗАЧЕМ?". Ответ простой - да, НЕЗАЧЕМ:)

Наверное, есть задачи, где надо заводить экземпляр класса Singleton, "...а вот если не было обращений (ну не потребовалось что-то), то ничего не заведется и все будет тихо и спокойно... А вот вроде как статический класс будет существовать даже тогда, когда он может не понадобиться... и ух, как страшно съест памяти... " В общем, как-то я не могу вот так с ходу придумать такой задачи, чтобы применять именно СОЗДАНИЕ классов вместо статических классов.

И вот я например, тоже не вижу разницы между сложным Singleton наворотом и простым Singleton::doAction(). Вообще, статические классы (со статическими переменными) чрезвыйчано удобны еще и тем, что они предоставляют как бы "глобальные" переменные для любой области видимости. И хэндлер для БД тому яркий пример.

Начинающие программисты, столкнувшись с ООП, обязтельно натыкаются на необходимость использования (ну или хотя бы понимания) шаблонов проектирования. И это многих пугает. Мало того, что сама парадигма достаточно сложна для понимания, так еще и паттерны какие-то. И хотя они как раз и придуманы для облегчения использования парадигмы, на первых порах играют как раз обратную роль. А всё из-за того, что те, кто придумывают паттерны, давно в ООП как рыба в воде, соответственно и объясняют их со своей колокольни. И получается замкнутый круг. Не зная паттернов сложно грамотно использовать ООП парадигму, однако не зная ООП сложно понять эти самые паттерны.

А все как обычно крайне просто. Нужно только создать необходимую ассоциацию, как все встанет на свои места.

Вот сейчас я попробую объяснить пару шаблонов простым русским языком. Может что-либо и получится.

Начнем с синглтона. Singleton переводится с английского как "одиночка". Но это так, ремарка. Теперь перенесемся в реальную жизнь.

Допустим вы хотите получить водительское удостоверение (права). Вы должны пройти соответствующее обучение и сдать экзамены в госавтоинспекции. И когда вы это сделаете, вам выдаются права. Это не просто корочка, а право на управление транспортым средством.

А теперь представьте, что вы эту корочку ненароком потеряли. Ну или она пришла в негодность, да мало ли. Вам не нужно снова учиться, не нужно сдавать экзамены. Это уже все есть пожизненно. Вам нужно просто придти в ГАИ и получить дубликат. Тоесть результат когда то произведенных вами действий.

Это и есть суть "синглтон", тоесть "один раз". Необходимые для получения прав действия (обучение и сдача экзаменов) мы производим один раз в жизни. А результатом можем пользоваться сколь угодно мгого раз.

Теперь дальше, ближе к программированию. Вообще этот паттерн не являются паттерном ООП, как многие ошибочно полагают. Это паттерны проектировния или design patterns. Их можно использовать и помимо ООП и даже классов, в обычных функциях. Вот, чтобы было понятнее, с них и начнем. Вот допустим такая простенькая функция:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Function mySingleton ()
{
$time = microtime (true );

Return $time ;
}

Echo mySingleton ();
echo "
" ;
sleep (1 );
echo mySingleton ();

Если зпустить этот код, второй результат будет на секунду отличаться от первого. Ну это и понятно, не требует пояснений. Но вот если сделать так:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18

Function mySingleton ()
{
static $time ;

If(empty($time ))
$time = microtime (true );

Return $time ;
}

Echo mySingleton ();
echo "
" ;
sleep (1 );
echo mySingleton ();

то сколько бы раз теперь мы бы не запускали функцию, результат будет одинаковый. Тот, который был вычислен при первом обращении. Вот это и есть синглтон во всей своей красе, правда очень примитивный.

Ну а теперь непосредственно в ООП. Не будем уходить от темы, просто обернем первую функцию в класс:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22

class mySingleton
{
public $time ;


{
$this -> time = microtime (true );
}
}

$obj = new mySingleton ();
echo $obj -> time ;

Echo "
" ;
sleep (1 );

$obj = new mySingleton ();
echo $obj -> time ;

И тут, каждый раз, когда мы создем объект, свойство $time будет принимать новое значение. Это иногда очень вредно, особенно при коннекте к базе данных. Если его поместить в конструктор, то подключение к бд будет происходить при каждой инициализации, а для скрипта обычно этого бывает достаточно сделать только единожды.

Но вернемся к нашим бранам. Как же нам сделать синглтон из класса? Чтобы получать только один объект, сколько бы раз мы к нему не обращались. Такая возможнсть появилась вместе с появлением статических классов. Вернее с возможностью использовать два в одном. Обращться к классу, как к таковому (статика) и создвать на его основе объекты. Вот мы сейчас заставим наш класс инициализировать и возвращать собственный объект:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

Class mySingleton
{
public $time ;


{
return new self ;
}

Public function __construct ()
{
$this -> time = microtime (true );
}
}


echo $obj -> time ;

Echo "
" ;
sleep (1 );

$obj = mySingleton :: getInstance ();
echo $obj -> time ;

Обратите внимание, снаружи мы получили объект без конструкции new , обртившись к статическому методу класса через двойное двоеточие. Однако объектом он от этого быть не перестал. Хотя и результат не изменился.

Однако мы же знаем, что объект можно легко поместить в переменную. И тем более в свойство. Ну а его можно объявить статичным. И вот мы плавно подошли к реализации синглтона. Делаем по образу и подобию нашей второй функции:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32

class mySingleton
{
public $time ;
static private $instance ;

Public static function getInstance ()
{

Return self :: $instance ;
}

Public function __construct ()
{
$this -> time = microtime (true );
}
}

$obj = mySingleton :: getInstance ();
echo $obj -> time ;

Echo "
" ;
sleep (1 );

$obj = mySingleton :: getInstance ();
echo $obj -> time ;

Вот и все, посмотрите на результат. Мы имеем то, что было вычислено при инициализации первого объекта. Еще бы, ведь это он и есть. Теперь один единственный, сколько бы раз мы его не дергали. Однако он будет так себя вести только при условии, что мы будем получать объект через статический метод. Но ведь есть еще возможность создать объект и с помощью конструкции new . А значит все наши труды насмарку. Так вот, чтобы загарантироваться от подобных казусов, нужно объявить конструктор приватным. Тогда конструкция new вне клаасса вызовет фатальную ошибку. Ну и на всякий случай запретим клонирование от греха подальше.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

class mySingleton
{
public $time ;
static private $instance ;

Public static function getInstance ()
{
if(empty(self :: $instance ))
self :: $instance = new self ;

Return self :: $instance ;
}

Private function __construct ()
{
$this -> time = microtime (true );
}

Private function __clone (){}
}

$obj = mySingleton :: getInstance ();
echo $obj -> time ;

Echo "
" ;
sleep (1 );

$obj = mySingleton :: getInstance ();
echo $obj -> time ;

Ну вот и все, мы имеем одну из самых распространенных реализаций паттерна Singletone

Что можно добавить. Использовать этот паттерн нужно с большой осторожностью, у него есть несколько недостатков. Он сродни глобальной переменной, он нарушает принцип SRP ну и так далее. Это тема отдельной публикации.

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

Просто не забывайте принцип KISS и используйте все в меру.

В следующий раз постараюсь рассказать еще несколько "знаменитых" паттернов, а пока всё)))

Singleton Pattern (single - единственный [англ.] ton - мода, стиль [англ.]) - один из наиболее известных шаблонов проектирования. Почти в любой объктно-ориентированной программе обычно существует один-два объекта, которые инициализируются в начале, и используются на всем протяжении работы приложения. Нет нужды каждый раз создавать объект класса, достаточно создать один экземпляр класса, в начале работы программы, и пользоваться им.

В РНР5 singleton определяется так

Пример использования

doAction(); ?>

Преимущества данного метода очевидны:

  1. Мы используем ссылки на один и тот же экземпляр класса в разных частях проекта. Таким образом не нужно создавать новый объект каждый раз когда мы хотим воспользоваться каким-то методом - просто пользуемся ссылкой.
  2. Теперь не нужно передавать объект как аргумент, чтоб все части программы были «в курсе» о текущем состоянии объекта. Все ссылки указывают на один объект.

PHP 5.3

Начиная с PHP 5.3.0 была добавлена возможность , благодаря которой этот паттерн можно реализовать в виде абстрактного класса:

Удачного вам кодинга!