Singleton php примеры. Паттерны в PHP (Singleton)

Введение

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

В качестве примера может служить класс для хранения установочных параметров(Settings). Settings class – хороший пример шаблона Singleton, потому что его данные не измены (единственный путь изменения установочных параметров это редактирование файла установочных параметров) и часто используются в различных частях приложения. Кроме того, создание новых объектов класса Settings, где это необходимо, требует ресурсы, что расточительно, т.к. объекты будут идентичны.

Определение

Шаблон Singleton предполагает наличие статического метода для создания экземпляра класса, при обращении к которому возвращается ссылка на оригинальный объект.

Пример для PHP5

Пример для PHP5(без реализации конкретных методов класса Settings)
class Settings {
private $settings = array();
private static $_instance = null;
private function __construct() {
// приватный конструктор ограничивает реализацию getInstance ()
}
protected function __clone() {
// ограничивает клонирование объекта
}
static public function getInstance() {
if(is_null(self::$_instance))
{
self::$_instance = new self();
}
return self::$_instance;
}
public function import() {
// ...
}
public function get() {
// ...
}
}

Реализация шаблона Singleton

Ключoм реализации шаблона Singleton является статическая переменная, переменная чье значение остается неизменным при исполнении за ее приделами. Это позволяет сохранить объект оригинальным между вызовами статического метода Settings::getInstance(), и возвратить ссылку на него при каждом последующем вызове метода.
Имейте так же в виду, что конструктор, как правило, приватный. Что бы обеспечить использование всегда только одного объекта Settings мы должны ограничить доступ к конструктору, что бы при попытке создания нового объекта возникала ошибка. Так же следует иметь в виду, что данные ограничения не возможны в PHP4.

Паттерн 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.

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

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

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

Начнем с синглтона. 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 была добавлена возможность , благодаря которой этот паттерн можно реализовать в виде абстрактного класса:

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

Singleton (синглтон) — один из простейших для понимания шаблонов проектирования в PHP. Это обычный класс в PHP, в логику которого добавлена проверка на единственность создания его экземпляра.

Как создать синглтон:

  • хранить экземпляр класса в приватной статической переменной
  • инициализировать его через метод getInstance и сохранять в статической переменной, а если она была создана раньше, то вернуть ее
  • определить метод __construct в private и определить в нем логику создания instance
  • определить метод __clone (клонирование объекта) как private
  • определить метод __wakeup (вызывается перед unserialize) как private

Почему стоит использовать синглтон:

  • позволяет иметь общую точку доступа к внешнему ресурсу
  • упрощает инициализацию, проверку состояния, передачу контекста в приложении
  • при грамотном использовании (примеры ниже), упрощает управление приложением

Когда стоит использовать Singleton:

  • подключение к БД
  • класс, инициализирующий настройки приложения, состояние, контекст

Почему не стоит использовать синглтон

  • нарушает принцип единственной ответственности (проверяет на существование, создает экземпляр, отдает результат)
  • затрудняет тестирование, т.е. приносит в приложение глобальное состояние
  • скрытые зависимости

Пример

Рассмотрим использование Singleton на примере создания соединения к базе данных MySQL.

Создадим класс DB. Информацию о соединении будем хранить в статической приватной переменной $_instance. Чтобы получить ее значение будем использовать статический метод getInstance(), в котором будем делать проверку на null переменной $_instance, в случае истины создавать ее через new self, иначе — возвращать ее.

Пример реализации этого класса:

_instance = new PDO("mysql:host=" . self::DB_HOST . ";dbname=" . self::DB_NAME, self::DB_USER, self::DB_PASS, ); } private function __clone () {} private function __wakeup () {} public static function getInstance() { if (self::$_instance != null) { return self::$_instance; } return new self; } }

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

mob_info