почему существуют классы java singleton? Когда вам нужно использовать один

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

Спасибо,


person Elliott    schedule 12.02.2011    source источник
comment
обеспечение того, чтобы у вас когда-либо был только один экземпляр класса, очень важно для вещей, которые привязаны к жизненному циклу приложения (то есть вам всегда нужен только один экземпляр некоторых конкретных классов на запуск В таком случае будет ошибкой два экземпляра такого класса дважды, но также очень грубая ошибка программиста, чтобы сделать возможным более одного такого экземпляра). Некоторые люди используют для этого синглтон, но основная проблема в том, что это усложняет тестирование, особенно модульное.   -  person SyntaxT3rr0r    schedule 12.02.2011
comment
@ SyntaxT3rr0r: большинство экземпляров на самом деле не должны быть привязаны к жизненному циклу приложения, а скорее к жизненному циклу модуля или конкретной области (например, области запроса для веб-приложений). В противном случае повторное использование часто невозможно, например код из автономного приложения в веб-приложении. // Если задуматься, использование ClassLoader для гарантии семантики синглтона во многих случаях действительно является странной идеей. Просто напишите обычный класс (т.е. дайте возможность создавать его несколько раз), а затем позвольте модулю внедрения зависимостей гарантировать семантику Singleton в пределах области видимости.   -  person Chris Lercher    schedule 13.02.2011


Ответы (11)


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

Зачем использовать синглтоны?

Вы можете погуглить "синглтон", чтобы найти самые разные причины. Из JavaWorld:

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

Зачем использовать синглтон вместо класса со всеми статическими методами?

Несколько причин

  1. Вы можете использовать наследование
  2. Вы можете использовать интерфейсы
  3. Это упрощает модульное тестирование самого одноэлементного класса.
  4. Это позволяет проводить модульное тестирование кода, зависящего от синглтона.

Для # 3, если ваш синглтон был пулом соединений с базой данных, вы хотите убедиться, что ваше приложение имеет только один экземпляр, но выполнить модульное тестирование самого пула соединений с базой данных, не затрагивая базу данных (возможно, используя конструктор области пакета или статический метод создания):

public class DatabaseConnectionPool {
  private static class SingletonHolder {
    public static DatabaseConnectionPool instance = new DatabaseConnectionPool(
        new MySqlStatementSupplier());
  }

  private final Supplier<Statement> statementSupplier;

  private DatabaseConnectionPool(Supplier<Statement> statementSupplier) {
    this.statementSupplier = statementSupplier;
  }

  /* Visibile for testing */
  static DatabaseConnectionPool createInstanceForTest(Supplier<Statement> s) {
    return new DatabaseConnectionPool(s);
  }

  public static DatabaseConnectionPool getInstance() {
    return SingletonHolder.instance;
  }

  // more code here
}

(обратите внимание на использование инициализации по запросу держателя шаблон)

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

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

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

person NamshubWriter    schedule 12.02.2011

Класс только со статическими методами (и частным конструктором) - это вариант, в котором нет никакого экземпляра (0 экземпляров).

Синглтон - это класс, для которого существует ровно 1 экземпляр.

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

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

person Arjan Tijms    schedule 12.02.2011
comment
Если бы вы беспокоились о ленивой инициализации (почему ???) и не хотели, чтобы инициализация происходила при инициализации класса (почему бы и нет ???), то вы могли бы перенести инициализацию во вложенный класс. В этом маловероятном случае потребуется некоторая пересылка на стороне реализации, но с синглтоном клиентский код должен делать это на каждом сайте использования. - person Tom Hawtin - tackline; 12.02.2011
comment
Если бы вы так беспокоились о ленивой инициализации (почему ???) - Прежде всего, проблема не в самой ленивой инициализации, а в связанных с этим расходах, связанных с необходимостью выполнять синхронизированную проверку при каждом вызове. Это вызывает конфликт блокировок и может снизить вашу потенциальную пропускную способность. - person Arjan Tijms; 12.02.2011
comment
Вы также можете инициализировать состояние класса в статическом инициализаторе (static { ... }). - person Paŭlo Ebermann; 12.02.2011
comment
не хотел, чтобы инициализация происходила при инициализации класса (почему бы и нет ???) - хм, инициализация, конечно же, происходит при инициализации. Это в самом значении этого слова. Дело в том, когда это происходит. С синглтоном это происходит, когда экземпляр обычно запрашивается у фабрики. В этот момент может произойти инициализация. Однако после получения экземпляра клиенты могут удерживать этот экземпляр, и никакая дальнейшая инициализация или проверки инициализации производиться не будут. - person Arjan Tijms; 12.02.2011
comment
Вы также можете инициализировать состояние класса в статическом инициализаторе - правда, вы могли бы, или вы могли бы использовать современный вариант с перечислением и его ctor. Дело в том, что статические инициализаторы могут быть довольно грубым механизмом. Они очень жестко запрограммировали инициализацию этого блока, который должен иметь все данные внутри класса для его работы. Хорошо, это может зависеть от еще одного статического метода в каком-то классе для возможного получения некоторых данных (самый простой из них, например, System.getProperty ()), но дело в том, что это работает не во всех случаях. - person Arjan Tijms; 12.02.2011
comment
@arjan Вы можете использовать идиому инициализации по требованию, чтобы не выполнять синхронизированную проверку при каждом вызове (см. cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#dcl). Очень хороший момент, когда нужно поместить все данные в блок инициализации. Вы также должны беспокоиться о том, что блок инициализации генерирует исключения, из-за которых класс не загружается. - person NamshubWriter; 13.02.2011

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

person Eddie    schedule 12.02.2011

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

person tvanfosson    schedule 12.02.2011
comment
Уравновешивание необъяснимого d / v - person Mark Peters; 12.02.2011

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

Например, представьте, что у вас есть синглтон поставщика соединений, который создает соединения с БД.

public class DBConnectionProvider implements ConnectionProvider {}

Если бы это был класс со статическими методами, вы не могли бы внедрить зависимость, например:

public void doSomeDatabaseAction(ConnectionProvider cp) {
   cp.createConnection().execute("DROP blah;");
}

Это должно быть

public void doSomeDatabaseAction() {
   DBConnectionProvider.createConnection().execute("DROP blah;");
}

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

person Mark Peters    schedule 12.02.2011

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

Обычно синглтон создается так, синхронизируясь с getInstance:

public class Singleton {

    private static Singleton instance;

    private Singleton(){
         // create resource here
    }

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

        return instance;
    }
}

Но так же справедливо и создать его так,

public class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton(){
        // create resource here
    }

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

Оба метода создадут единственный экземпляр загрузчика классов PER.

person Joel    schedule 12.02.2011
comment
Если синглтон, который вы создаете, основан на интерфейсе, и вы используете Java 1.5+, подумайте об использовании enum. Например public enum Singleton implements SingletonContract { INSTANCE; /* interface method implementations and such here */ } - person Esko; 12.02.2011
comment
@Joel, следует отметить разницу между Singleton и singleton. Некоторые называют Singleton - шаблоном GOF (который вы описываете) и сценариями его использования. Это можно обсудить, но этот шаблон GOF иногда считают антипаттерном. Класс, имеющий ровно один экземпляр, называется singleton. Singletons можно легко заменить классами с единственным экземпляром. - person Alex Nikolaenkov; 12.02.2011
comment
@Alex - Вы имеете в виду нормальный класс, но с использованием контейнера IOC, чтобы гарантировать создание только одного экземпляра? Совершенно верно. Но шаблон Singleton по-прежнему полезен, и его стоит знать. Это настолько хороший шаблон, что многие новые языки, такие как Scala, встроили его в язык. - person Joel; 12.02.2011
comment
Боюсь, что первый пример не является поточно-ориентированным и с несколькими загрузчиками классов. Статические методы синхронизируются с объектом класса, но для разных загрузчиков классов будут разные объекты класса, поэтому синхронизация не будет работать должным образом. Однако я не могу придумать элегантного обходного пути. - person Sergei Tachenov; 12.02.2011
comment
@Sergey - Спасибо, что указали на это, я соответственно обновил. - person Joel; 12.02.2011
comment
У этих двух фрагментов кода есть ключевое различие; они создают экземпляр в разных точках. Если конструктор дорогостоящий, а объект часто вообще не нужен приложению, может быть лучше использовать первый фрагмент (исходя из принципа, позволяющего нести расходы тем, кто в нем нуждается). - person Donal Fellows; 13.02.2011

Синглтон имеет преимущество перед классом со статическими переменными и методами: это экземпляр объекта, который может наследовать от класса (пример: приложение с одним основным JFrame) и расширять один или несколько интерфейсов (и, таким образом, рассматриваться как любой другой объект, реализующий эти интерфейсы).

person JB Nizet    schedule 12.02.2011
comment
Поскольку это экземпляр, его также можно высмеивать. Имитация статических функций требует больше усилий. - person KitsuneYMG; 12.02.2011
comment
@KitsuneYMG Значит, макет будет вторым экземпляром вашего синглтона? - person Tom Hawtin - tackline; 12.02.2011
comment
Создание синглтона дает вам возможность выбирать между реализациями в методе getInstance () - для тестирования возвращается макет, в противном случае - реальный. Как вы это делаете - другой вопрос. - person Axel; 12.02.2011

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

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

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

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

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

person Sergei Tachenov    schedule 12.02.2011

Когда вам нужно его использовать? Есть много объектов, которые нам нужен только один из: пулы потоков, кеши, диалоговые окна, объекты, которые обрабатывают предпочтения и параметры реестра, объекты, используемые для ведения журнала, и объекты, которые действуют в качестве драйверов устройств для таких устройств, как принтеры и графические карты. Для многих из этих типов объектов, если бы нам пришлось исследовать более одного, мы бы столкнулись со всевозможными проблемами, такими как некорректное поведение программы или чрезмерное использование ресурсов.

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

person Bartzilla    schedule 12.02.2011
comment
Я бы не сказал, что синхронизация вообще не нужна. Если синглтон предоставляет доступ к общему ресурсу, тогда должна иметь место некоторая синхронизация. Я согласен, что вам не обязательно все синхронизировать. - person Sergei Tachenov; 12.02.2011
comment
@Sergey - Я согласен, я пытаюсь разоблачить, что «если в многопоточном окружении», как говорится в вопросе; тогда просто синхронизация использования в некоторых случаях будет не самой лучшей. Конечно, вы можете это сделать, если создание экземпляра не вызывает значительных накладных расходов в приложении. Но если это так, вы можете уменьшить это влияние на производительность, используя другие параметры, такие как переход к готово созданному экземпляру или «двойная проверка блокировки», чтобы уменьшить использование синхронизации. - person Bartzilla; 12.02.2011
comment
к сожалению, DCL полностью нарушен в Java. - person Sergei Tachenov; 12.02.2011
comment
@Sergey - Если вы используете JVM, отличную от Java 5, рассмотрите другие методы (кроме DCL) для реализации Singleton. Шаблоны проектирования - прежде всего. Правило 5: Работа с многопоточностью. Вот поток, который показывает, как реализовать DCL в Java 5 или выше Как решить DCL в java взято из Effective Java, Second Edition, что в основном является целью DCL - person Bartzilla; 12.02.2011

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

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

person Bradley Swain    schedule 12.02.2011

Они также используются с другими узорами. См. Можно ли создать наблюдаемый класс как синглтон ?

person simpatico    schedule 30.04.2011