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

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 (синглтон) — один из простейших для понимания шаблонов проектирования в 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; } }

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

Введение

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 Pattern - один из наиболее известных шаблонов проектирования . Почти в любой объектно-ориентированной программе обычно существует один-два объекта, которые инициализируются в начале, и используются на всем протяжении работы приложения. Таким объектом может быть, logger - объект для ведения log-ов (log в переводе с англ. - журнал). Так в случае с log-ами, нам нет нужды каждый раз создавать объект класса logger, лишь для того, чтобы произвести запись в журнал. Достаточно создать один экземпляр класса, в начале работы программы, и пользоваться им. Такие классы (как например logger) будем называть Singleton классами.

Важно отметить, что Singleton Pattern также решает еще одну наболевшую задачу. Часто бывает так, что мы передаем в функцию (метод) параметр лишь для того, чтоб эта функция передала этот параметр еще дальше. Или же несколько частей программы постоянно нуждаются в «свежей» информации о состоянии объекта, который меняется по ходу работы; тогда снова и снова приходиться передавать объект как параметр.

Конечно, если такая ситуация возникает пару раз за всю работу приложения, можно не обращать на это внимания. Но, если один и тот же объект бестолково «футболяется» между всеми модулями и функциями программы, может возникнуть страшная путаница и, как следствие, ошибки. В таких ситуациях следует использовать Singleton класс. Вместо того, чтоб передавать по цепочке объект из функции в функцию, Singleton Pattern обеспечивает возможность, каждой части программы, получить по ссылке единственный для всей программы объект.

Вот какие преимущества дает Singleton Pattern :

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

Еще раз сформулируем суть Singleton Pattern : Убедиться, что существует только один экземпляр класса и обеспечить доступ к нему из любой части программы.

Недостаток «глобального» похода

Итак, мы хотим получить только одного представителя класса и использовать его везде, где нам заблагорассудится. Казалось бы, логично ввести глобальную переменную и не загружать себе голову всякими pattern -ами. Но тут есть подвох.
Представим, что мы решили использовать глобальную переменную, содержащую singleton объект. Все нужные нам свойства есть - переменная одна и доступна отовсюду. В чем же тогда проблема? Проблема в том, что глобальная переменная непредсказуемая: у нас нет гарантии, что, на протяжении работы приложения, в ней содержится тот же объект, который мы инициализировали в начале.

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

Как создать singleton класс

Очень просто. В классе необходимо создать статический метод getInstance (), который возвращает единственный экземпляр класса. Если этот метод вызывается первый раз, то экземпляр класса создается, сохраняется в приватном (private ), статическом (static ) поле, и возвращает свеже-созданный объект. При повторном использовании метода, возвращается содержимое поля (т.е. экземпляр класса).
В следующем примере реализовано все вышесказанное.

Метод getInstance() - суть Singleton pattern . С помощью getInstance () мы можем получить доступ к объекту класса logger откуда угодно. В тоже время, методы construct () и clone (), объявленные как приватные (private ), обеспечивают единственность экземпляра класса logger. Удален источник возможных ошибок - разработчик не может случайно создать еще один объект такого же типа (Такой же принцип действует и при построении value object - убрать, спрятать все методы, с помощью которых, объекты смогут <размножаться>). Следовательно, getInstance ()- единственный способ <достучаться> к представителю класса.

Что же вышло в итоге

Итак, нам удалось создать singleton класс. Мы обеспечили единственность экземпляра класса и организовали удобный доступ к нему. Что же мы выиграли, используя singleton pattern?

  • Не нужно опасаться, что наш единственный объект «подменится» по ходу работы приложения. Он надежно защищен от случайностей.
  • Из любой части программы можно легко получить объект и пользоваться им. (и вовсе не нужно рассылать его, как параметр, всем функциям или создавать каждый раз заново, чтоб использовать какой-то метод).

P.s.

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

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

Вероятно также, что в случае с PHP данная проблема вообще не актуальна, т.к. задачи типа создания многопотоковых приложений, или сетевых (когда один экземпляр класса используется несколькими приложениями одновременно) перед 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.