Почему существуют классы Java Singleton?Когда вам нужно использовать один - PullRequest
27 голосов
/ 12 февраля 2011

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

Спасибо

Ответы [ 11 ]

14 голосов
/ 12 февраля 2011

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

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

Вы можете Google "синглтон", чтобы найти все виды причин. От JavaWorld :

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

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

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

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

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

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 с помощью метода package-scope createInstanceForTest.

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

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

4 голосов
/ 12 февраля 2011

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

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

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

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

1 голос
/ 12 февраля 2011

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

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

public class DBConnectionProvider implements ConnectionProvider {}

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

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

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

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

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

1 голос
/ 12 февраля 2011

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

1 голос
/ 12 февраля 2011

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

Как правило, вы создаете синглтон, например, синхронизируясь по 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 загрузчика классов.

1 голос
/ 12 февраля 2011

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

0 голосов
/ 30 апреля 2011

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

0 голосов
/ 12 февраля 2011

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

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

0 голосов
/ 12 февраля 2011

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

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

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

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

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

0 голосов
/ 12 февраля 2011

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...