Аудитория: Java Junior, любители холиворов, профессиональные написатели синглтонов



Любые замечания и предложения — очень приветствуются. Это мое первое видео, и не совсем понятно, нужен ли тут вообще такой контент. Считайте это закрытым альфа-тестом, только для посетителей хаба Java :)


Ниже дана полная текстовая расшифровка, кому не хочется тратить время на просмотр.


Вступление


Привет, Хабр! Наступил вечер, и нам пора серьезно поговорить. Хочу с тобой обсудить стыдное. Cинглтоны.


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


Сначала о том, почему синглтоны – это стыдно.


Старейшая книга в которой говорится о синглтоне (ну по крайней мере, самая старая, какую видел своими глазами) написана в 1994 году. Это книга «Паттерны проектирования» Банды Четырех: Гамма, Хелм, Джонсон, Влиссидес.
Просто задумайтесь, какая это древность. Чем вы занимались в 1994 году? Кое-кто из наших коллег в этом году еще не родился.



Или вот, вторая любовь моей жизни – книга «Test Driven Development» Кента Бека, написанная в 2002 году.



И вот что написано про синглтоны там:



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



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


Светлая сторона


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


Глобальное состояние


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


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


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


Явное и неявное


Существует известный принцип, согласно которому явное лучше неявного. Этот принцип, кстати, заложен в PEP 20, более известный как "дзен языка Python".



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


The Single Responsibility Principle


Принцип единственной ответственности. То есть это буква S в аббревиатуре SOLID. Эта аббревиатура – важнейшая в жизни любого джависта. Я так ее уважаю, что хочу сделать наколку с ней.


Этот принцип когда-то ввел Роберт Мартин (более известный как Дядя Боб).



Он утверждает, что каждый объект должен иметь одну ответственность и эта ответственность должна быть полностью инкапсулирована в класс.


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


Сильная связанность


Все компоненты, использующие синглтон, или не дай бог – общающиеся через него, мгновенно оказываются жестко связанными. Связанное зачастую приходится развязывать — например, чтобы написать тесты. И, тестировать код, густо обмазанный синглтонами, очень неприятно. Даже один-единственный синглтон может серьезно напрячь вообще всех: и тестировщиков, и программистов с TDD, и подгадить на проведении демонстраций изолированной функциональности.


Специфика Java


В Java нет специального способа записывать синглтон. Поэтому существует множество способов записать его, и все они некрасивые.


Во-первых, все можно впихать в статическое поле или енум.


public class Singleton {
    public static final Singleton INSTANCE = new Singleton();
}

public enum Singleton { INSTANCE; }

Но это будет не ленивый вариант, он нам не нужен.
Хотя енум таки можно сделать ленивым, если хочется.


Можно засунуть всё под synchronized:


public class Singleton {
    private static Singleton instance;

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

Но это будут тормоза, кои собственно и символизирует synchronized, в самом типичном случае использования.


Или приходится использовать double-checked locking вариант:


public class Singleton {
    private static volatile Singleton instance;

    public static Singleton getInstance() {
        Singleton localInstance = instance;
        if (localInstance == null) {
            synchronized (Singleton.class) {
                localInstance = instance;
                if (localInstance == null) {
                    instance = localInstance = new Singleton();
                }
            }
        }
        return localInstance;
    }
}

Выглядит он еще более мерзко. Плюс отношение к этой форме записи выразили сами разработчики языка Java:


"There exist a number of common but dubious coding idioms, such as the double-checked locking idiom, that are proposed to allow threads to communicate without synchronization. Almost all such idioms are invalid under the existing semantics, and are expected to remain invalid under the proposed semantics."


Темная сторона


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


Глобальное состояние есть везде


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


Как минимум, глобальная переменная – это наш реальный мир, в котором мы все живем. Если у нас какая-то компьютерная игра, MMORPG, мы ее пишем, то единый виртуальный мир игры – это тоже глобальное состояние. У нас не будет другой жизни и другого мира.


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


Аргумент про порядок тестов тут тоже не сработает: очевидно, что при исполнении, первой должна подниматься наша единственная база (и выполняться тесты относительно базы), и потом уже прикладной код. Если поднять код раньше базы, то все упадет. Большинство проектов, которые я видел, явным образом запускали базу до кода, и не видели в этом вообще никаких практических проблем.


"It just works" лучше явного


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


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


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


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


Жесткое лучше мягкого


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


Что касается тестирования. Если посмотреть, как это в реальности, то во многих проектах тестирование либо вообще не делают, либо делают в очень урезанном виде. В проекте может быть тысячи классов, но всего несколько десятков тестов. И ради этого крошечного количества вообще не имеет смысла беспокоиться, и переписывать систему особым образом. Это касается не только синглтонов, а вообще всего – настоящее полномасштабное тестирование тянет за собой очень много дополнительных условий. На практике ставить во главу угла тестируемость – не всегда хорошая идея.


Специфика Java


Да, написание синглтона выглядит некрасиво. Но в IDE всегда можно создать шаблон «некрасивого» класса, и навсегда забыть о том, чтобы писать этот неприятный код вручную. Как говорится, темнота – друг молодежи.


Решение


И вот, мы оказались на пороге реального конфликта: одни люди хотят синглтоны (на самом деле, просто глобальное состояние, просто они его называют синглтоном), а другие – наоборот сильно против этого.


Отличным решением является переход от настоящих синглтонов к сиглтонам курильщика… ой ой. Singleton Beans из Spring. В чем суть: с помощью аннотации Component и Scope(SCOPE_SINGLETON) вы помечаете некоторые классы как синглтоны.


import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class Greeter {
    public String hello() {
        return "Hello World!";
    }
}

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


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloWorldController {
    @Autowired
    Greeter greeter;

    @RequestMapping(path = "/")
    public String home() {
        return greeter.hello();
    }
}

Заметьте, что этот вариант решает все перечисленные выше проблемы.


  • Оно позволяет использовать глобальное состояние, но при этом не запирает вас в рамках одного контекста. Вы в Спринге можете делать сколько угодно контекстов. Если необходимо сделать отдельный контекст для тестирования, это делается в несколько строк кода. Более того, оно предоставляет удобные инструменты для работы с контекстом, наспример, можно получить список всех существующих сейчас синглтонов. Как это сделать в чистой джаве – наверное, никак.
  • Оно позволяет явно описать все существующие сейчас синглтоны. Но не тратить время на ручное управление зависимостями. То есть, в коде присуствует большая магия, но эта магия полностью контролируется, если нужно.
  • Оно не нарушает S в SOLID, потому что жизненным циклом управляет Spring
  • Оно выглядит красиво и лаконично, так как сводится к нескольким аннотациям, и вообще не заставляет писать boilerplate код.

Резюме


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


И еще


И раз уж вы не только посмотрели видос, но и дочитали до конца. Спасибо, за поглощение моего контента, оказали мне этим большую честь. (серьезно.) А теперь что с вас нужно: обязательно оставьте комментарий к этой статье, поставьте лайк, подпишитесь на хаб Java на Хабре, на блог друганов из JUG.ru которые замотивировали меня выложить этот горячечный брейндамп в сеть, и на других видных видеоблоггеров.


Пока!


Комментарии (79)


  1. jreznot
    06.08.2017 21:30
    +3

    А вам не кажется, что масштабы трагедии преувеличены?


    1. olegchir Автор
      06.08.2017 21:35
      +4

      Десятками лет эту проблему мусолят. Совпадение? :)


      1. izzholtik
        06.08.2017 23:11
        +3

        Проблему goto мусолят ещё дольше и активнее. Совпадение? ;)


        1. igordata
          07.08.2017 02:51
          +3

          Не думаю!


          1. bolk
            07.08.2017 07:54
            +1

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


            1. Comdiv
              07.08.2017 19:32
              +4

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


              1. bolk
                08.08.2017 07:13
                +2

                Вы смешиваете две проблемы в одну.

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

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


                1. Comdiv
                  08.08.2017 11:39
                  +2

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

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

                  С исключениями тоже интересно, учитывая тенденции новых языков. В Go избыточное использование исключений сделали неудобным, поэтому даже не все знают, что там они есть, а в Rust исключений нет вовсе. Со Swift ещё не разобрался, но, похоже, его разработчики тоже пытаются найти что-то своё. Всё быстро меняется и Ваше понятие о современном программисте могло и устареть, не говоря о том, сколько современных программистов используют С. Думаете, они поголовно мучаются без исключений?


                  1. bolk
                    08.08.2017 11:52
                    +2

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


                    1. Comdiv
                      08.08.2017 12:19
                      +1

                      Напишите статью на эту тему, раз так, общественность скажет «спасибо»!

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

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

                      То есть, они все перешли на C++? Вроде бы нет же, не переходят. Зачем они «мучаются»?


                      1. bolk
                        08.08.2017 12:34
                        +1

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


                  1. bolk
                    08.08.2017 11:55
                    +1

                    О том, что break и continue — это частные случаи goto знают не только избранные, и ругают точно также как и goto.
                    Беглый поиск по «Хабру» не подтверждает вашу точку зрения. goto не ругает только ленивый, break/continue вспоминают значительно реже.


                    1. Comdiv
                      08.08.2017 12:47
                      +1

                      Мой беглый поиск вообще показал, что goto, в основном, оправдывают.


        1. laphroaig
          07.08.2017 17:38
          +3

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


    1. franzose
      07.08.2017 09:29
      +3

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


  1. hazard2
    06.08.2017 22:40
    +2

    А зачем явно указывать scope? Controller же по умолчанию имеет scope Singleton.


    1. olegchir Автор
      06.08.2017 22:55
      +1

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


      1. hazard2
        06.08.2017 23:05
        +2

        Я просто не понимаю: почти любой bean в спринге — это синглтон, так как это настройка по умолчанию. А как вообще предлагается на java без синлтонов? Без спринга вообще? Или предлагается менять scope всех компонентов?


        1. olegchir Автор
          06.08.2017 23:35
          +2

          Ну вообще-то да, есть неспринговые люди и проекты. Тут недавно как раз была статья «Почему я ненавижу Spring». https://habrahabr.ru/post/334118/.

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


          1. ohotNik_alex
            07.08.2017 09:53
            +3

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


        1. edge790
          07.08.2017 19:32
          +2

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


    1. nonnenmacher
      07.08.2017 09:17
      +1

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


  1. asm0dey
    06.08.2017 23:05
    +2

    Олег, я правильно понял что ты пришёл к тому, что синглтон норм если использовать IoC?


    1. olegchir Автор
      06.08.2017 23:33
      +2

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


      1. asm0dey
        07.08.2017 00:13
        +3

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


        1. olegchir Автор
          07.08.2017 09:19

          Да и в Скале тоже, в каком-то смысле


        1. franzose
          07.08.2017 09:31
          +2

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


          1. asm0dey
            07.08.2017 09:32
            +2

            Ну он может решить почти любые проблемы при правильном дизайне приложения


            1. franzose
              07.08.2017 09:33
              +1

              Не совсем понял. Какие?


              1. asm0dey
                07.08.2017 09:34
                +1

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


                1. franzose
                  07.08.2017 09:36
                  +2

                  Ну так это получается не синглтон проблемы решает, а Spring)


                  1. asm0dey
                    07.08.2017 09:39
                    +1

                    Но вам не нужен спринг чтобы написать своё приложение на синглтонах же. То же самое можно сделать с Dagger2, например, или с Petite.


            1. drwatson1
              08.08.2017 17:38
              +2

              Ага, кроме проблемы слишком большого количества синглтонов )))


        1. fogone
          07.08.2017 10:13
          +1

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


          1. asm0dey
            07.08.2017 11:04
            +1

            А почему не имеет состояния? Мне кажется нам ничто не мешает в него запихать `@Volatile` переменную и смело с ней работать.


            1. fogone
              07.08.2017 13:12
              +1

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


    1. Deosis
      07.08.2017 07:10
      +3

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


  1. guai
    07.08.2017 01:51
    +4

    это уже не синглтон, а service loader
    и в чистой яве есть одноименный класс для этого


  1. GrimMaple
    07.08.2017 03:16
    +11

    Выскажу свое скромное мнение.

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

    В целом весьма интересно смотреть, и я с радостью посмотрел бы другие видео, если они будут покороче :)


    1. diafour
      07.08.2017 10:11
      +1

      На скорости 1.5х нормально было смотреть.


    1. olegchir Автор
      07.08.2017 12:52
      +3

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

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

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


      1. GrimMaple
        07.08.2017 18:26
        +3

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


  1. fogone
    07.08.2017 07:21
    +4

    Инжекшн в поля — это же фу.


    1. fogone
      07.08.2017 10:23
      +1

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


      1. edge790
        08.08.2017 12:47
        +1

        а для опенсорс проектов можно просто писать с ломбоком и делать деломбок


        1. Borz
          08.08.2017 16:02
          +1

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


  1. Develop_up
    07.08.2017 09:17
    +1

    В андроид Сингелтоны нужны?


    1. Revertis
      07.08.2017 12:34
      +1

      Application, если переопределен вами, уже синглтон. Создается системой один раз и живет всё время существования приложения в памяти. Сервисы тоже синглтоны. Вы не можете запускать их несколько экземпляров сразу. И так далее.
      Плюс, как уже заметил автор, какие-нибудь «хранители подключения к базе» или другому ресурсу вполне могут быть синглтонами. Только непонятно зачем там instance как таковой, если можно сделать статический класс со своим методом init() или load(), запускаемым при старте приложения.


  1. unit4
    07.08.2017 09:18
    +2

    Проблема всех таких постов, в том, что люде говорят:«ай яй яй, так делать низя...», а как можно не говорят. Пример со Spring не показателен, так как не во всех проектах он есть. Вот было бы хорошо, если бы кто то написал статью, что то вроде «Как спасти мир от singleton» и парочку примеров из жизни с и без него.


    1. olegchir Автор
      07.08.2017 09:18

      Рассказать, как добавить Спринг в проект? :)


      1. unit4
        07.08.2017 14:02
        +1

        Не, мне не надо, я имел ввиду, есть у меня проект, какой то свой, в вакууме, без всех этих спрингов и гуав, и нужен мне синглтон, вдруг… а шо делать?) Вот ниже, franzose и fogone говорят «пользуй инъекцию», но, это нас возвращает опять ко всяким ДепенденсиИнъекциям, в которых, прошу заметить, вы все равно говорите как инъецировать, синглтоном или нет. Так может синглтон и не зло, просто его нужно правильно писать и пользовать?

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


        1. franzose
          07.08.2017 14:35
          +2

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


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

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


    1. franzose
      07.08.2017 09:32
      +3

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


    1. fogone
      07.08.2017 10:17
      +2

      В самых простых случаях даже не обязательно использовать di-контейнер, можно «инжектить» в конструктор самому. Насоздавал все нужные инстансы в одном методе и раздал их в конструкторы, вот и вся недолга.


  1. msts2017
    07.08.2017 12:43
    +2

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


    1. franzose
      07.08.2017 14:37
      +1

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


      1. RomanPokrovskij
        07.08.2017 17:07
        +1

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


        1. franzose
          08.08.2017 01:21
          +1

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


          1. RomanPokrovskij
            08.08.2017 02:57
            +2

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

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


            1. franzose
              09.08.2017 00:53

              Проще всего, мне кажется, использовать DI-контейнер.


              1. RomanPokrovskij
                09.08.2017 21:46

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

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

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

                И когда удобней переменная в глобальной области видимости, это значит что удобней переменная в глобальной области видимости. Никто не передает Math.Round через DI.


      1. poxvuibr
        08.08.2017 13:03
        +1

        Иии у Роберта Мартина есть на это готовый ответ :) .


  1. RomanPokrovskij
    07.08.2017 12:47
    -3

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


    1. RomanPokrovskij
      07.08.2017 15:00

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

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


  1. nestor_by
    07.08.2017 14:02
    +1

    public class Something {
        private Something() {}
    
        private static class LazyHolder {
            static final Something INSTANCE = new Something();
        }
    
        public static Something getInstance() {
            return LazyHolder.INSTANCE;
        }
    }

    https://en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom


    1. franzose
      07.08.2017 14:38
      +1

      Как эта реализация решает проблему сильной связанности?


      1. nestor_by
        07.08.2017 15:37
        +2

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


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


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


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


  1. sasha1024
    07.08.2017 15:29
    +3

    Огромное спасибо за транскрипт.


  1. Iqorek
    07.08.2017 16:02
    +2

    Есть паттерн синглтон и его реализация такова

    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 лучше не использовать.


    1. andres_kovalev
      08.08.2017 17:39

      Поддерживаю


  1. vladvic
    07.08.2017 23:19
    +1

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


    1. franzose
      08.08.2017 01:23
      +1

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


  1. webaib1
    08.08.2017 17:39
    +3

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


    1. esin
      12.08.2017 22:23

      А какой смысл получать везде один и тот же объект, если он stateless?


      1. fogone
        13.08.2017 09:34

        Лучше поставить вопрос по-другому: какой смысл получать разные объекты, если у них всё равно нет состояния и соответственно проблем совместного использования? Например Comparator без параметров, есть ли смысл его каждый раз инстанцировать?


        1. esin
          14.08.2017 12:53

          Если у есть объекты, которые вам по сути не нужны (за исключением их методов, раз уж они stateless), то заведите себе статический util класс с этими методами.
          Синглтон если уж и делать, то как раз для stateful объекта, чтобы везде был доступен один и тот же объект с одним и тем же состоянием.


          1. fogone
            14.08.2017 17:48

            заведите себе статический util класс с этими методами

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

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


          1. edge790
            14.08.2017 18:16

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


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


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


  1. vyatsek
    11.08.2017 14:28

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