LINUX.ORG.RU

Зачем нужен синглетрон?

 ,


1

3

Зачем нужен шаблон «проектирования» синглетрон? Почему про него везде пишут, почему я его ни разу не применял? Зачем он нужен, для чего?

class OneInstance{

   static $ya = null; 
    
   static function factory(){
       if(!self::$ya){
            return self::$ya = new self;       
       }else{
           return self::$ya;
       }
   }

}  

★★★★

Очень удобен для классов, хранящих всякую мелочёвку вроде содержимого конфигов. Без синглтона пришлось бы всюду таскать с собой указатели на этот класс. Это уныло.

Deleted
()
Ответ на: комментарий от resurtm

Расскажи ещё что-нибудь, ты так интересно повторяешь за другими. Хотя в данном контексте можно согласиться, ещё бы в php синглетоны тащить. Чаще всего их применяют не от хорошей жизни и не от большого ума, конечно.

Nuna
()

ЗЫ синглеТрон?! Чей на трон?!

erfea ★★★★★
()
Ответ на: комментарий от Nuna

Мне ни разу не понадобились. Приведи пример, где они РЕАЛЬНО нужны.

resurtm ★★★
()

Зачем он нужен, для чего?

Очень хорошая вещь для сохранения и использования глобального состояния. Переменные WEB Request, плугин манагер, контейнер сервисов... Синглетон объявляет себя глобальным авторитетом, а другой код его в этой роли использует. Так как он обычно определяется именем класса, сделать ошибку в его использовании намного сложнее, чем, например, просто глобальной переменной. Очень полезная штука.

anonymous
()
Ответ на: комментарий от i-rinat

Когда код изначально пишется как многопоточный, конечно.

Пример: был код, использовал внутри пару вложенных вызовов, на третьем уровне было использование синглтона. Изредка этот вложенный вызов что-то в синглтон писал. Изначально код синхронный, через несколько месяцев встал вопрос оптимизации в связи с ростом количества пользователей сервиса, распараллелили этот кусок кода, повесили мьютексы на то, что напрямую использовалось функцией.

В итоге имеем редкий плавающий баг, который в большой кодовой базе трудно локализовать.

Когда кодовые базы маленькие и команда небольшая, проблемы в общем-то нет, но с ростом численности строк и сотрудников у такой баги сильно вырастают шансы уйти незамеченной от code review.

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

Глобальный изменяемый стейт — это вообще страшная дичь, но такое в ынтырпрайзах сплошь и рядом.

mersinvald ★★★★★
()
Последнее исправление: mersinvald (всего исправлений: 1)

Синглтон — это тупо глобальная переменная. В пхп и вообще вебе принято вместо этого хранить стейт в БД (тупо потому, что он должен быть общим для 9000 машин с кодом).

inb4 конфиги: синглтон для них них не нужен, константы и без него прекрасно работают.

x3al ★★★★★
()
Последнее исправление: x3al (всего исправлений: 1)
Ответ на: комментарий от i-rinat

Сигнглтон меня (и тс-а, его «синглетрон» волнует) щас вообще не интересует. Меня интересует многопоточность в пехепе.

Deleted
()
Ответ на: комментарий от mersinvald

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

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

Deleted
()
Ответ на: комментарий от x3al

синглтон для них них не нужен, константы и без него прекрасно работают

Синглон является одним из способом запихнуть все переменные в некое пространство имён, чтобы не фигачить однообразные префиксы к каждому имени переменной, чтобы избежать конфликтов.

Второе преимущество конкретно в PHP - имена класса не требуют global для доступа из функции. Соответственно, не нужно объявлять все конфигурационные переменные, к которым собираешься обращаться.

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

Что касается многопоточности, то в PHP с ней очень легко работать - её просто нет :-) Так что подобные подводные камни синглтонов для PHP в принципе не актуальны. В какой-нибудь Java достаточно навершать synchonized где нужно, либо использовать thread local storage (в зависимости от того, нужно ли шарить состояние синглтона между потоками). В C++ это делать сложнее, но C++ сам по себе сложнее, это норма.

Кстати, ещё есть такой вариант - синглтон может быть совмещён с паттерном стратегия. Например, мы имеем какие-то платформозависимые функции. Пишем абстрактный класс с прототипами этих функций. Пишем реализации под все платформы, наследующие этот класс. В базовом классе делаем метод getInstance, который детектит платформу и создаёт нужного потомка, а затем возвращает (повторные вызовы возвращают сохранённый инстанс, заново тратить время на детект не нужно). В этом случае синглтон может вообще не иметь полей, только методы (но не обязательно). Главная фишка будет в том, что getInstance может вернуть разные реализации, но при этом на детект потратит время только один раз. Конечно, в случае PHP можно просто инклюдить разные файлы в зависимости от платформы (ничто не мешает совместить это с предыдущим методом и перед созданием класса инклюдить его файл в getInstance после детекта). Однако, требование реализовывать платформозависимые методы именно в классе позволяет иметь гарантии, что мы ничего не забыли реализовать (потому что мы унаследуем базовый класс и если забудем переопределить какой-нибудь абстрактный метод, то получим ошибку ещё на этапе создания его экземпляра, а не при обращении к отсутствующей функции, которая может вызываться редко и только при некоторых условиях), а ещё жёстко задать интерфейс.

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 3)
Ответ на: комментарий от KivApple

фигачить однообразные префиксы

Что? Есть неймспейсы. Но насчёт конкретно пхп не уверен.

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

Синглтон — это просто глобальный инстанс одного класса + (не слишком нужная) проверка на то, что другой инстанс не сделать. Этот инстанс является глобальной переменной, поэтому ничего нового не даёт.

на детект потратит время только один раз

Это тупо мемоизация, есть сотни способов реализации этого, в том числе и те, что переживут реквест. Хотя я не в курсе, как это принято в пхп.

x3al ★★★★★
()
Ответ на: комментарий от x3al

Этот инстанс является глобальной переменной, поэтому ничего нового не даёт

Открою тебе секрет - со времён изобретения процедур ничего принципиально нового в программировании не появлялось. Всё можно реализовать на базе более простых примитивов. Но это не делает ООП и другие вещи ненужными - это синтаксический сахар, с ним код становится выразительнее и понятнее.

Это тупо мемоизация, есть сотни способов реализации этого

Это не только лишь это. Это ещё и проверка интерфейса.

Смотри:

function foo() {
...
}

function bar() {
...
}

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

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

А можно сделать так:

class AbstractPlatform {
    abstract function foo();
    
    abstract function bar();

    static function getInstance() {
        ...
    }
}

А затем:

class WindowsPlatform extends AbstractPlatform {
    function foo() {
        ...
    }
    
    function bar() {
        ...
    }
}

class LinuxPlatform extends AbstractPlatform {
    function foo() {
        ...
    }
    
    function bar() {
        ...
    }
}

Соответственно, getInstance при первом вызове детектит платформу, подгружает нужный класс, создаёт его экземпляр, сохраняет на будущее и возвращает.

Какие мы получаем профиты?

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

Это самый очевидный для меня пример. Можно придумать ещё, но мне лень.

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 4)
Ответ на: комментарий от KivApple

Очевидный пример почему синглетоны имеют плохую славу. Спасибо, весьма доходчиво. Но есть ещё вариант приложения с графическим интерфейсом, где синглетоны имеют право на жизнь, не забывайте.

Nuna
()
Ответ на: комментарий от KivApple

Только вот при написании новой реализации придётся постоянно подсматривать в реализации для других платформ - «а какие ещё функции я забыл реализовать».

В php всё настолько ужасно с статической проверкой типов и конкретно с интерфейсами?

Вообще, это выглядит ужасным костылём и я бы матерился, увидев этот типа-синглтон без состояния ибо понять, что в конкретном случае он — вместо мемоизации detectPlatform() и находится в том, что должно быть интерфейсом.

x3al ★★★★★
()

Забей, синглтон — это очередной жабокостыль, появившийся из-за того, что жабка настолько убогая, что даже глобальные переменные не умеет, которые в реальных проектах таки нужны, что бы там фанатики-пуристы, разрабатывающие стандарт, не визжали. Само собой разумеется, в других языках жабокостыли нафиг не нужны. Разработчики стандарта и за инкапсуляцию долго визжали, да так, что нарушать её при надобности пришлось через жопу рефлексию, да ещё и оптимизацию в JRE делать, чтобы рефлексия безбожно не тормозила. И за типизацию визжали, ввёв в итоге автовывод типов. И глобальные переменные в жабке когда-то появятся, не переживай.

Но! похапэ тоже говно, не забывай, что global без вариантов зашкваривает zval-контейнер проставленным рефом, сбросить который уже нельзя. Так что если не хочешь сайд-эффектов от рефа, как-то внезапные копирования там, где без рефа бы CoW копирование предотвратил, и внезапные мутации того, что мутировать не нужно — юзай жабовские синглтоны, ну или сри в $_GLOBAL.

bodqhrohro_promo
()
Ответ на: комментарий от bodqhrohro_promo

Ты про pthreads, для которого надо пересобирать похапу?

Да, но есть же и готовые сборки на самом деле.

aiive
()
Ответ на: комментарий от aiive

Покажи. Я вот давеча искал PHP5 под Debian 9, хоть какой-нибудь — не нашёл. И из сырцов сконпелять не осилил, они с новыми хедерами не дружат, надо патчить. Поставил в итоге 7-й, благо, кода, завязанного на 5-й, там пока запускать не приходилось.

bodqhrohro_promo
()

Простой пример для PHP – работа с объектом доступа к БД в процессе обработки HTTP-запроса, когда ГАРАНТИРОВАННО нужно создавать только один такой объект, причем конфигурацию соединения с БД и его установление хочется закрыть для всего остального кода (инкапсулировать все эти дела внутри соответствующего класса), чтобы потом было проще сопровождать приложение.

Тут синглтон — вполне адекватный прием. Где нужно поработать с БД, просто получаешь объект и работаешь.

vinvlad ★★
()
Ответ на: комментарий от vinvlad

Тут синглтон — вполне адекватный прием

как насчёт нет? Все беды начинаются когда выбирают не тот паттерн. Завтра тебе понадобятся 30 синглтонов на разных уровнях.

Nuna
()
Ответ на: комментарий от Nuna

как насчёт нет? Все беды начинаются когда выбирают не тот паттерн. Завтра тебе понадобятся 30 синглтонов на разных уровнях.

Поясни своё возражение. Откуда возьмутся 30 синглтонов, если я заведомо работаю с одной конкретной СУБД-шкой на уровне SQL (другого по определению не предвидится) и мне просто хочется ограничить в правильном направлении свободу тех, кто потом будет дорабатывать и сопровождать программный код (а заодно и сделать код более читабельным)? О каких разных уровнях идет речь?

vinvlad ★★
()
Ответ на: комментарий от vinvlad

с одной конкретной СУБД-шкой на уровне SQL

сегодня. Завтра другие могут принять решение интегрировать эту кодовую базу куда-то ещё и никто всё переписывать не будет. А ещё у них тоже были гениальные идеи по поводу использования синглотонов, да и вообще.

Nuna
()
Ответ на: комментарий от Nuna

Завтра другие могут принять решение интегрировать эту кодовую базу куда-то ещё ...

Интересно куда? Это ведь только в теории все используют только стандартизированное подмножество SQL. Если же речь идет о серьезном, высокоэффективном приложении, то возможности конкретной СУБД-шки используются на полную катушку. ORM при работе с реляционными СУБД используют только дилетанты (и учат этому тоже дилетанты) и последствия принятия таких «мудрых» решений весьма плачевные.

Даже если, к примеру, кому-то потом ну очень приспичит перескочить на другую RDBMS, то класс-то останется одним - просто в него нужно будет зашивать специфику конкретных СУБД. От разумной доработки кода по-любому никуда не деться, если хочется, чтобы приложение нормально работало при высокой нагрузке. Правда, если речь идет о высокой нагрузке и эффективности, то сначала выбирают CУБД, а потом уже начинают программировать.

vinvlad ★★
()
Ответ на: комментарий от vinvlad

Эх, не убедительно. Как тут помогут синглтоны? С одной стороны я вижу предложение делегировать всю вообще всю релевантную логику в него (что теоретически поможет с логическим разделением в весьма ограниченном юзкейсе и плохо для всего остального), и в то же время тут же предложение перелопачивать всю эту логику при появлении новых требований. О приложениях какого масштаба идёт речь?

Каким боком тут ORM вообще не ясно. Целесообразность использования ORM тема для совершенно отдельной беседы, но отказываться от него по религиозным причинам явно не стоит и профессионалы это понимают. Не стоит делать такие громкие заявления.

Nuna
()

синглетроны - любимые конструкции начинающих прграмммистов. когда пецсать правильно не умеют или не хотят.

anonymous
()
Ответ на: комментарий от vinvlad

когда ГАРАНТИРОВАННО нужно создавать только один такой объект, причем

нонче не учат делать геттеры в классах? делаешь геттер: если поле не определено, то создаёшь новый объект и назначаешь в поле, возвращаешь поле.

anonymous
()
Ответ на: комментарий от Nuna

Завтра другие могут принять решение интегрировать эту кодовую базу

кодовую базу

много чести для ваншотного быдлокода

anonymous
()
Ответ на: комментарий от Deleted

Очень удобен для классов, хранящих всякую мелочёвку вроде содержимого конфигов.

Вот да, конфигурация программы - чуть ли не единственный пример, где оправдан глобальный объект.

hobbit ★★★★★
()
Ответ на: комментарий от Nuna

О приложениях какого масштаба идёт речь?

О приложениях, где требуются сложные SQL-запросы и, естественно, требуется оптимизация этих самых запросов. Такие вещи сильно привязаны к конкретной СУБД-шке, поэтому об автоматическом переносе кода на другую СУБД-шку речи в принципе не идет. Отсюда и отказ от ORM, поскольку в случае ORM вы вообще теряете контроль над SQL-кодом и можете заполучить какую-то чужую, усредненную и заведомо неэффективную реализацию, которая будет приемлемо работать только на небольших тестовых данных (религия здесь ни при чем).

На делегировании всей релевантной логики в одном классе я, в общем-то, не настаиваю - речь изначально шла об использовании фиксированной СУБД. К примеру, такой класс может инкапсулировать в себе доступ на уровне на PDO. Если вам нужно делать код в расчете на несколько СУБД-шек, то можно, конечно, ввести отдельный интерфейсный уровень, в который запрятать SQL-специфику, но здесь уже можно не городить лишние синглтоны - зачем?

синглетроны - любимые конструкции начинающих программистов.

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

нонче не учат делать геттеры в классах? делаешь геттер: если поле не определено, то создаёшь новый объект и назначаешь в поле, возвращаешь поле.

Видимо, нынче не учат тому, что создавать лишний объект только для того, чтобы потом обратиться к его геттеру - это лишний гемморой и трата вычислительных ресурсов по сравнению с вызовом фиксированного статичного метода.

vinvlad ★★
()
Ответ на: комментарий от Nuna

Примечание:

... Если вам нужно делать код в расчете на несколько СУБД-шек, то можно, конечно, ввести отдельный интерфейсный уровень, в который запрятать SQL-специфику ...

По сути, такой интерфейсный уровень может быть уже уровнем модели в терминах MVC, поскольку потом может приспичить в контексте конкретной СУБД перенести часть логики в хранимые СУБД-шные процедуры )

vinvlad ★★
()

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

Железяка одна, требует точной работы с ней. Проект большой, и например не факт что именно ты дальше будешь пользовать класс_для_работы_с_ней. А именно объект данного класса.

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

И вот этот шаблон пользуется для защиты от такого рода.

Т.е. паттерн полезный, но именно там где он нужен. Т.е. пихать его всюду - это уже будет антипаттерн. Недавно смотрел видео с какой-то конфы - там чел говорил что синглтоны у них запрещены из-за того что они мешают им тестировать код. Т.е. если человек хочет в отдельном модуле создать экземпляр класса для тестов - то синглтон им помешает.

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

Например тут

Очень удобен для классов, хранящих всякую мелочёвку вроде содержимого конфигов. Без синглтона пришлось бы всюду таскать с собой указатели на этот класс. Это уныло.

Синглтон не нужен и только помешает тестировать класс конфигов.

bonta ★★★★★
()
Ответ на: комментарий от bonta

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

bonta ★★★★★
()
Ответ на: комментарий от anonymous

когда ГАРАНТИРОВАННО нужно создавать только один такой объект, причем

нонче не учат делать геттеры в классах? делаешь геттер: если поле не определено, то создаёшь новый объект и назначаешь в поле, возвращаешь поле.

Если речь об обычных методах объекта, то где гарантия, что только у одного экземпляра будет вызван этот геттер?

Если объект уникален, то он и есть синглетон.

Если это статические методы (в терминах C++, хз как они в php называются), то это и есть реализация синглетона.

German_1984 ★★
()

Конкретно в php такой необходимости не встречал, а вообще синглетон - это способ организации глобальной переменной так, чтобы она а) была гарантированно инициализирована перед использованием б) была инициализирована не более одного раза.

Обычно это хэндлеры всяких внешних объектов типа файлов, коннектов, устройств, окон...

German_1984 ★★
()
Последнее исправление: German_1984 (всего исправлений: 1)
Ответ на: комментарий от German_1984

Если объект уникален, то он и есть синглетон.

не надо передёргивать. синглтоном называют сущности, которые нарушают инкапсуляцию.

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

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

anonymous
()
Ответ на: комментарий от vinvlad

Такие вещи сильно привязаны к конкретной СУБД-шке, поэтому об автоматическом переносе кода на другую СУБД-шку речи в принципе не идет

... а добавить вторую такую же СУБД сбоку — вполне имеет смысл зачастую.

x3al ★★★★★
()
Ответ на: комментарий от anonymous

не надо передёргивать. синглтоном называют сущности, которые нарушают инкапсуляцию.

Откуда такие сведения?

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

German_1984 ★★
()

Теоретически антипаттерн. Практически нужен там, где имеется тяжеловесный объект, который создавать на каждый чих дорого в плане перфоманса. Часто такой тяжеловесный обїект - что-то типа константы.

cab ★★★★
()
Ответ на: комментарий от x3al

Такие вещи сильно привязаны к конкретной СУБД-шке ...

... а добавить вторую такую же СУБД сбоку — вполне имеет смысл зачастую.

Вы можете иметь несколько копий одних и тех же данных на разных БД-серверах одного типа (реплицировать данные) и при выполнении синглтона выбирать, к какому серверу подключаться. Здесь нет никаких противоречий - под «конкретной» СУБД-шкой я здесь подразумевал конкретный тип СУБД, а не экземпляр. Если хочется иметь реализацию, совместимую с СУБД-шками разных производителей, то здесь вопрос тоже решаемый в рамках обсуждаемого шаблона - просто тут решение будет немножко похитрее.

vinvlad ★★
()

Зачем он нужен

Увеличивать сложность кода, ЧСВ кодогенератора и затраты на поддержку.
Ничего ты в Хайскалабилити хайавалабилити энтерпрайз продакшен девелопмент солюшн паралел кластер платформ систем не понимаешь.

devl547 ★★★★★
()
Ответ на: комментарий от devl547

Ничего ты в Хайскалабилити хайавалабилити энтерпрайз продакшен девелопмент солюшн паралел кластер платформ систем не понимаешь.

У-у-у... сколько много умных слов ) А поконкретнее что-нибудь можешь сказать, кроме как «Ничего ты ... не понимаешь»? Здесь же люди по делу общаются, а не обмениваются пустыми, голословными фразами.

vinvlad ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.