Синглтон не только для конфигов
Singleton — первый шаблон проектирования о котором я узнал. Очень хорошо помню этот момент, по тому, что было это на собеседовании. Мое первое и, если честно, единственное техническое собеседование. Как уже догадался читатель, на вопрос «что такое синглтон и с чем его едят» я только удивленно моргал глазами. Правда, на работу меня все равно взяли. Может быть по тому, что после об]яснения мне в двух словах сути, общий механизм построения я описать смог.
Если что-то хочет познакомится с этим и гругими порождающими шаблонами, то можно обратиться к «банде четырех» или , ближе ко мне, Мэтт Зандстра – PHP. Объекты, шаблоны и методики программирования. Когда мы говорим про веб-разработку, то обычно мы слышим такие применения для Синглтона:
Первый пример
Первый, самый популярный, на мой взгляд, пример — подключение к базе данных. Это всегда казалось мне какой-то шляпой. Используя Синглтон мы гарантируем, что подключимся к база данных только один раз, а в дальнейшем будем использовать это подключение. Но что, если у нас несколько баз данных? «Ок,» — скажут знатоки — «если все реализовать правильно, то мы будем гарантировать, что к конкретной базе не будем подключаться более одного раза». Ну а если база одна, но коннектиться нужно от разных пользователей? «Ок,» — скажут знатоки, обозвав меня занудой и любителем придумывать проблемы — «ты можешь все написать так, что гарантируешь, что подключение к одной базе от имени одного пользователя будет единственно и его-то ты и будешь использовать дальше. И вообще суть в том, что ты сможешь использовать подключение в других классах не передавая его явно». А почему я не могу использовать global? Или просто статический метод? «Просто ты дурак и любишь поспорить. Это все гораздо хуже.» Это я слышал в разных интерпретациях много раз.
Второй пример
Второй пример — конфиг в синглтоне. Суть в том, что мы можем задать в нем любую переменную, а к ней геттер и сеттер. Тут суть иллюстрируется таким кодом:
$class1 = myClass::getInstance();
$class2 = myClass::getInstance();
$class2->setA(3);
$class1->setA(5);
echo $class2->getA(); //5
То есть мы всегда гарантируем, что в любой момент времени в любом классе переменная A будет единой для всей системы. Чем это отличается от глобальных переменных, про которые любой новичок скажет, что «Так писать нельзя» ? Мне всегда было непонятно.
Другие примеры мне долгое время не встречались и я не понимал, нафига тогда этот синглтон нужен на самом деле и почему он делает код лучше. И вот сегодня ко мне пришло откровение
Мой вариант
В одном из наших проектов сложная система расчета цен для разных пользователей. Все началось с того, что все пользователи делятся на несколько ценовых групп, в каждой из которых есть своя наценка на каждую группу товаров. Позже оказалось, что есть поставщики с другой политикой. Они предоставляют прайс «для всех», а диллерам и постоянным покупателям дают от него скидки. Соответственно модель наценок на каждую товарную группу превратилась в модель наценка-или-скидка. Но и это еще не все. Есть клиенты (и их много), которые давно работают с нашими заказчиками и у них есть особые условия – специальная скидка или наценка на конкретный товар или группу товаров.
Я вот сейчас только в общих чертах описал идею, а уже целый абзац получился. В общем, все эти цены нужно считать, да еще и выводить «на лету». Все бы ничего, но это довольно крупный интернет-магазин и из-за системы фильтров может оказаться, что пользователь запросил сразу кучу товаров и все с разным алгоритмом расчета цены. Кеширование не сильно спасает по производительности, т.к. сайт будет очень «живой» и кеш придется постоянно пересчитывать из-за меняющихся условий.
Итак, та-да-а-ам. Меня спас Синглтон. Паттерн гарантировал мне, что я лишь однажды запрашиваю из базы информацию о наценках/скидках, товарах и клиентах. А обрабатываю её по мере надобности. Так как запрос из базы самая «толстая» операция и мне удалось сократить количество запросов с примерно 80 до 3-4, мы получили прирост в производительности расчета примерно в 10 раз.
На практике это означает, что все стало «летать». И все благодаря тому, что мы не делаем лишних запросов к базе, за счет Синглтона.
Может быть, кто-то из читателей приведет свой пример успешного использования этого паттерна не с гипотетической пользой, а с реальной? Было бы интересно обсудить.