Комментарии 79
НЛО прилетело и опубликовало эту надпись здесь
Тем не менее, мы все годами в разных языках используем этот goto и ничего (continue, break или как оно называется в вашем языке — это всё goto). В goto всех беспокоила одна вещь, которой давно нет — это вход снаружи в тело других конструкций. Но почти никто уже не помнит что за проблема была и поэтому goto всех беспокоит до сих пор.
Всё несколько не так. Проблема вообще не в goto, а в неструктурном программировании. Если соблюдать принципы структурного программирования, то goto оказывается ненужным. При этом само по себе использование goto не приводит автоматически к неструктурному коду, и наоборот — без goto тоже можно программировать неструктурно. Поскольку большинству людей не хочется вникать в суть, то и мусолят они то, что лежит на поверхности — goto.
Вы смешиваете две проблемы в одну.

Все ругают goto и «знают», что оно плохо, но почти никто не ругает break/continue, хотя это то же goto. Такое расслоение как раз из-а того, что goto в некоторых языках позволяет входить внутрь конструкций. Реинкарнации в современных языках такого не позволяют.

Что касается структурного программирования, то проблема вовсе не в том, что людям в суть не хочется вникать, а в том, что оно попросту неудобно. Просовывать всюду кучу флагов для прерывания всех циклов вверх по уровням абстракций — это очень неудобно. И если с отсутствием break/continue современный программист ещё смирится, то с отсутствием исключений (которые суть те же навороченные goto) — точно нет.
О том, что break и continue — это частные случаи goto знают не только избранные, и ругают точно также как и goto. Те же, кто считает break и continue оправданными, и к goto относятся более тепло. И проблема неструктурного программирования не исчерпывается заходом внутрь конструкций. Поэтому, к примеру, в MISRA C — рекомендациях для критичных к ошибкам ПО, неструктурное программирование запрещено практически полностью, включая и преждевременный выход из функции.

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

С исключениями тоже интересно, учитывая тенденции новых языков. В Go избыточное использование исключений сделали неудобным, поэтому даже не все знают, что там они есть, а в Rust исключений нет вовсе. Со Swift ещё не разобрался, но, похоже, его разработчики тоже пытаются найти что-то своё. Всё быстро меняется и Ваше понятие о современном программисте могло и устареть, не говоря о том, сколько современных программистов используют С. Думаете, они поголовно мучаются без исключений?
При структурном программировании совсем не обязательно просовывать кучу флагов, если этого не требует сама задача. Речь об этом и неудобстве подтверждает то, что я сказал ранее — большинству людей не хочется вникать в суть.
Напишите статью на эту тему, раз так, общественность скажет «спасибо»! Меня структурному программированию учили в КГУ (ныне КФУ), вдруг незабвенный Самитов, который это преподавал о чём-то нам не рассказал.
В Go избыточное использование исключений сделали неудобным, поэтому даже не все знают, что там они есть.
Нигде не видел, чтобы авторы языка рассказывали о таком, на мой взгляд, они просто не очень-то продумали этот момент.
Всё быстро меняется и Ваше понятие о современном программисте могло и устареть, не говоря о том, сколько современных программистов используют С. Думаете, они поголовно мучаются без исключений?
Собственно результат их мучений — язык Си++.
Напишите статью на эту тему, раз так, общественность скажет «спасибо»!

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

А в каком виде он это рассказывал -«оно есть, но неудобно»?
Собственно результат их мучений — язык Си++

То есть, они все перешли на C++? Вроде бы нет же, не переходят. Зачем они «мучаются»?
К сожалению, мои наблюдения говорят об обратном. Легко можно получить тонну ненависти.
Ну а как можно продолжать этот разговор, если непонятно что вы имеете ввиду?
А в каком виде он это рассказывал — «оно есть, но неудобно»?
У меня в дипломе записано, что я «математик-системный программист», нас учили программировать, структурному программированию тоже учили, естественно, как без этого?
То есть, они все перешли на Cи++? Вроде бы нет же, не переходят
Собственно, я сам пишу на Си, а на Си++ не пишу и не собираюсь, это долгий вопрос почему, об него сломано много копий — есть множество статей на эту тему, почему Си++ всё же не торт. А вот на Гоу, например, я программирую охотнее, чем на Си. Хотя всё, что хочется на нём не напишешь.
О том, что break и continue — это частные случаи goto знают не только избранные, и ругают точно также как и goto.
Беглый поиск по «Хабру» не подтверждает вашу точку зрения. goto не ругает только ленивый, break/continue вспоминают значительно реже.
Мой беглый поиск вообще показал, что goto, в основном, оправдывают.
goto обычно ругают, ну или делают сноску с предупреждением практически во всех книгах начиная с 90-х, а синглетон в книгах — это паттерн, а значит это хорошо, кроме того для начинающего разработчика это еще и просто, понятно и прикольно. Плюс еще куча в статей в сети про потокобезопасные синглетоны и разные варианты реализации. А вот про то, что на практике синглетон используют как злобный архитектурный костыль, пишут мало.

Масштабы трагедии обычно в том, что из-за синглтонов возникает жесткая связанность компонентов. Как следствие — невозможность нормально всё это дело тестировать.

Можно и не указывать. В примере исключительно для того, чтобы лучше проиллюстрировать
Я просто не понимаю: почти любой bean в спринге — это синглтон, так как это настройка по умолчанию. А как вообще предлагается на java без синлтонов? Без спринга вообще? Или предлагается менять scope всех компонентов?
Ну вообще-то да, есть неспринговые люди и проекты. Тут недавно как раз была статья «Почему я ненавижу Spring». https://habrahabr.ru/post/334118/.

А еще у нас есть очень специальный человек, Егор Бугаенко, у него свой подход, и даже книжка имеется: http://www.yegor256.com/elegant-objects.html
был я на его лекции на Jpoint17. очень странный подход. для проектов на коленке без адаптивной структуры пойдет. но никак не для серьезных продуктов. в примере, который был показан для демонстрации всей красоты этого кода, было более 20 уровней вложенности конструктор-в-конструктор.
читаемость де-факто нулевая, но зато не магия.

Один из основных принципов Spring'а: "Convention over Configuration". Т.е. чтобы разработчикам нужно было делать как можно меньше однообразных действий, спринг практически для всего делает "дефолтную" конфигурацию.
Я думаю они просто решили сделать синглтон скоуп, потому что он лучше всего подходит под эту философию.

Автор не Controller, а Component приводит в статье. Но, думаю, это не особо критично в данном контексте, просто оставил комментарий, потому что автор попросил :)

Олег, я правильно понял что ты пришёл к тому, что синглтон норм если использовать IoC?
да :) но в разумных пределах. Когда в «реальном мире», в предметной области, есть глобальное состояние — мы тоже можем его использовать. Или когда это самое дешевое решение, в т.ч. в перспективе. А как помойку использовать все еще не норм.
Тогда, кажется, уместно начинать видео с текста о том, что синглтон в неумелых руках — это антипаттерн, а в умелых — мощное оружие, бьющее точно в цель и реализующееся в котлине ключевым слово «object».

Получается, что мощным оружием синглтон становится, если только «спрятан» в IoC. Иначе — жесткая связанность. Да и что в нём такого мощного? :)

Ну он может решить почти любые проблемы при правильном дизайне приложения
Те самые проблемы, которые решает приложение :) Ну правда, значительная часть приложений, написанных на спринге ничего кроме синглтонов и не имеет.
Но вам не нужен спринг чтобы написать своё приложение на синглтонах же. То же самое можно сделать с Dagger2, например, или с Petite.
Ага, кроме проблемы слишком большого количества синглтонов )))
object как в котлине хорош для реализаций, для которым достаточно одного инстанса и которые не имеют состояния (например компаратор какой-нибудь). Это хоть и синглтон в каком-то смысле, но он не имеет состояния и обычно может быть достаточно смело использован, хотя я временами и такие вещи выношу в конструктор, если считаю, что такая параметризация может иметь смысл. Еще object-ы можно использовать как контейнер для фабричных функций и тому подобные вещи без состояния. Делать же object-ы с состоянием это тоже самое, чтобы обычные синглтоны, только что более красиво, потому что язык скрывает ленивую инициализацию от глаз разработчика.
А почему не имеет состояния? Мне кажется нам ничто не мешает в него запихать `@Volatile` переменную и смело с ней работать.
я же не сказал, что нельзя, я сказал, что он object хорош для другого. Т.е. для обычного синглтона его конечно тоже можно использовать, просто не надо.

Если использовать DI контейнер, то объект и не узнает, что он является синглтоном.
Об этом вообще никто не узнает кроме контейнера.

НЛО прилетело и опубликовало эту надпись здесь
Выскажу свое скромное мнение.

Возможно, я немного из другого поколения, но видео мне кажется просто невероятно растянутым. 17 минут для меня очень долго, да и суть можно было бы уложить в куда более мелкий формат. Для лайв-стрима, может быть, было приемлемо, но для видео с возможностью редактирования, обрезки и монтажа я считаю 17 минут слишком большим количеством времени. Возможно, имело бы смысл написать статью и зачитать ее — тогда было бы меньше моментов, когда начинаете подбирать слова и получаются «эээ» и «ммм».

В целом весьма интересно смотреть, и я с радостью посмотрел бы другие видео, если они будут покороче :)
Про «другое поколение» — это было тонко. На самом деле, мычания там самый минимум, я очень четко представлял, что хочу сказать. Если его вырезать, размер видео не уменьшится.

Это типичная проблема технодокладов — ты начинаешь рассказывать какую-то элементарщину на 5 минут, но пара иллюстраций — и он еле упихивается в стандартные 50 минут, причем выбросить что-то затруднительно.

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

да и для закрытых тоже. По сути, lombok нужен только на этапе написания кода же

Application, если переопределен вами, уже синглтон. Создается системой один раз и живет всё время существования приложения в памяти. Сервисы тоже синглтоны. Вы не можете запускать их несколько экземпляров сразу. И так далее.
Плюс, как уже заметил автор, какие-нибудь «хранители подключения к базе» или другому ресурсу вполне могут быть синглтонами. Только непонятно зачем там instance как таковой, если можно сделать статический класс со своим методом init() или load(), запускаемым при старте приложения.
Проблема всех таких постов, в том, что люде говорят:«ай яй яй, так делать низя...», а как можно не говорят. Пример со Spring не показателен, так как не во всех проектах он есть. Вот было бы хорошо, если бы кто то написал статью, что то вроде «Как спасти мир от singleton» и парочку примеров из жизни с и без него.
Не, мне не надо, я имел ввиду, есть у меня проект, какой то свой, в вакууме, без всех этих спрингов и гуав, и нужен мне синглтон, вдруг… а шо делать?) Вот ниже, franzose и fogone говорят «пользуй инъекцию», но, это нас возвращает опять ко всяким ДепенденсиИнъекциям, в которых, прошу заметить, вы все равно говорите как инъецировать, синглтоном или нет. Так может синглтон и не зло, просто его нужно правильно писать и пользовать?

Я к чему весь этот базар-вокзал развел, к тому что я, как раз тот кто начинает постигать великий и могучий Java и я вот только и вижу, что все хают синглтон, но никто не дает реальные примеры как быть без него. Не используй и все тут, а неопытному уму и не вдомек, а как же, вот у нас есть и в Collections.singletonList
и что, его не использовать теперь? А оказывается, это не тот самы синглтон, что из GoF, а совсем иной.
Собственно и хочу, что бы один раз вразумили, что синглтон это все же не плохо, просто исторически так сложилось…

Синглтон обличают не только в Java. Т.к. паттерн особо к языку не привязан.


Так может синглтон и не зло, просто его нужно правильно писать и пользовать?

А как иначе, если не с использованием контейнера?

Использовать инъекцию зависимости вместо прибивания её гвоздями.

В самых простых случаях даже не обязательно использовать di-контейнер, можно «инжектить» в конструктор самому. Насоздавал все нужные инстансы в одном методе и раздал их в конструкторы, вот и вся недолга.
Еслиб не было реальной потребности в синглтонах их бы не существовало. В книге 1994 года синглтон был норм, а в книге 2001 стало плохо, ааааа, книга то про тестирование, собственно тут становится неудивительно почему, мешает тестированию — значит плохо, а если не мешает?, не так уж и плохо?, т.е. проблема не в синглтонах а в их применении? или даже больше — в кривой организации тестирования?

Что посоветуете делать, чтоб и протестировать, и синглтон заюзать?

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

Не совсем понятно, как это поможет избавиться от захардкорженных зависимостей. Если честно, я не умею Java, можно пример?)

Выделить весь текст, удалить, смотреть на белый экран. Вы избавились от захардкоженных зависимостей.

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

IoC контейнер.
Не всегда проще но никогда не просто.

1) Ломается стройность API. Видно на пример EF Core. Вместо того чтобы создать контекст и передать в конструктор параметры. Создаешь контекст, достаешь контейнер и параметры пихаешь в контейнер. Хорошо когда есть и контйнер и конструктор (передача по стеку). А значит не просто.
2) Бардак вылезает из других мест, начинаются всякие per thread, xml configuration. И дебаг значительнос сложнее.

Я использую и IoC контейнер (получить конфиграцию в «юзер коде», но это может быть просто функция `Resolve` передаваемая по стеку).

И когда удобней переменная в глобальной области видимости, это значит что удобней переменная в глобальной области видимости. Никто не передает Math.Round через DI.
«глобальное состояние» — нет такого термина. есть состояние и есть переменная в глобальной области видимости. синглтон это неизменяемая переменная в глобальной области видимости.результатом борьбы с синглетонами отвратительные догматеры которые делают стойку на самые примитивные вещи. Напримр. Переменная может быть и функцией. Math.Round(0.3333, 2) — это еще синглетон, но ничего не мешает создать динамически (через eval, expression trees etc) функцию-синглетон СonfigurableMath.Round(0.3333) таким образом что парметр «2» берется из конфигурации и делается вызов в Math.Round(0.3333, 2). Ну и чем СonfigurableMath.Round хуже Math.Round?

Ну так а чем вам условная СonfigurableMath.Round (синглетон делегат/функция, сгенерированная рантайм при первом использовании из параметров) хуже условного Math.Round (статический метод) объяснит кто-нибудь?

Если ситуация не знакома и вычурна разирсую: функция может быть сложнее, например сериализации (не потоковая, а именно конструируется проход по полям так что сериализация вообще без reflection, так быстрее потоковой на процентов 50-20)? Почему рантайм а не сохранить сгенерированную функцию в файл? Не надо управлять кодом.

Эта реализация дает то что описано в заголовке Специфика Java.


Она простая понятная и совершенно ленивая.


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


Вот хорошая статья здесь на хабре как раз по этому поводу https://habrahabr.ru/post/334636/

Есть паттерн синглтон и его реализация такова
public final class Singleton {
    private static final Singleton INSTANCE = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return INSTANCE;
    }
}

Singleton pattern
и больше никакова :)
А есть объект синглтон (по нашему одиночка), то есть один объект в системе, но при этом не обязательно чтобы он был создан с помощью паттерна Singleton.
То что вы называете синглтоном курильщика это скорей реализация паттерна dependency injection. Поэтому вопрос синглтонов, но объектов да решен, паттерн DI решает проблемы которые создает паттерн Singleton, при этом дает вам возможность иметь один инстанс объекта в системе, если это нужно. При этом паттерн Singleton лучше не использовать.
Я могу сказать как определился с синглтонами я.
Я пользуюсь синглтонами. Но чтобы ограничить скоуп, я назначаю ссылку на instance в конструкторе самого синглтона, а сам синглтон создаю где-то в мэйне. Это даёт 1) возможность управлять порядком инициализации синглтонов, в том числе если какие-то из них вдруг зависят друг от друга. 2) возможность управлять порядком их разрушения (полезно например в С++), ну и 3) стандартная возможность синглтонов — доступ из любого места кода.
Иными словами синглтон не создаётся по запросу instance, а создаётся как обычный объект, разница только в том что создаться может лишь один экземпляр, и доступ к нему может быть отовсюду.

Ну, если прокидывать его в конструктор другого класса как зависимость, то это нормально. А если повсеместно писать ::getInstance, то потом тестить это невозможно)

Какая то наркомания с этими синглтонами — это просто паттерн, гарантирующий, что вы получаете везде один и тот же объект.
Создавать этот паттерн надо так, что он был мокующимся (инъекции и т.д.)
То что некоторые пихают в эти объекты состояния — это проблема исключительно этих персон и не дай им сделать это, они найдут другой способ отстрелить себе ногу.
А какой смысл получать везде один и тот же объект, если он stateless?
Лучше поставить вопрос по-другому: какой смысл получать разные объекты, если у них всё равно нет состояния и соответственно проблем совместного использования? Например Comparator без параметров, есть ли смысл его каждый раз инстанцировать?
Если у есть объекты, которые вам по сути не нужны (за исключением их методов, раз уж они stateless), то заведите себе статический util класс с этими методами.
Синглтон если уж и делать, то как раз для stateful объекта, чтобы везде был доступен один и тот же объект с одним и тем же состоянием.
заведите себе статический util класс с этими методами

В теории, конечно, статический метод можно превратить в компаратор ссылкой на этот метод, но компаратор — это ведь далеко не единственное, что не имеет состояния. Например стратегии часто не имеют состояния, их вы тоже будете статическими методами делать?

А вот что касается синглтона с состоянием, то я совсем перестал их использовать. Только иногда ThredLocal и то, по возможности стараюсь избегать.

Не всегда. статик Util класс не так просто "замокать" как синглтон.
Удар по перфомансу от синглтона-бина небольшой, бОльшая часть этого удара случается при поднятии контекста(старте аппликейшена, если это НЕ ленивая инициализация).


В юнит-тестах желательно тестировать только один класс, чтобы не было "каскадов ошибок" когда неправильно отрабатывает один, а за ним падают все на него ссылающиеся.


Поэтому Spring'овые синглтоны порой следует использовать даже для stateless классов.

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