Синглтон с аргументами в Java - PullRequest
128 голосов
/ 27 июня 2009

Я читал статью о Синглтоне в Википедии и наткнулся на этот пример:

public class Singleton {
    // Private constructor prevents instantiation from other classes
    private Singleton() {}

    /**
     * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
     * or the first access to SingletonHolder.INSTANCE, not before.
     */
    private static class SingletonHolder { 
        private static final Singleton INSTANCE = new Singleton();
    }

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

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

public class Singleton
{
    private static Singleton singleton = null;  
    private final int x;

    private Singleton(int x) {
        this.x = x;
    }

    public synchronized static Singleton getInstance(int x) {
        if(singleton == null) singleton = new Singleton(x);
        return singleton;
    }
}

Спасибо!


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

// AbstractTask implements Serializable
public class Task extends AbstractTask
{
    private final ReferenceToReallyBigObject object;

    public Task(ReferenceToReallyBigObject object)
    {
        this.object = object;
    }

    public void run()
    {
        // Do some stuff with the object (which is immutable).
    }
}

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

// AbstractTask implements Serializable
public class Task extends AbstractTask
{
    private static ReferenceToReallyBigObject object = null;

    private final String filePath;

    public Task(String filePath)
    {
        this.filePath = filePath;
    }

    public void run()
    {
        synchronized(this)
        {
            if(object == null)
            {
                ObjectReader reader = new ObjectReader(filePath);
                object = reader.read();
            }
        }

        // Do some stuff with the object (which is immutable).
    }
}

Как видите, даже здесь у меня проблема в том, что передача другого пути к файлу ничего не значит после того, как пропущен первый. Вот почему мне нравится идея магазина , которая была размещена в ответах. В любом случае, вместо того, чтобы включать логику загрузки файла в метод run, я хотел абстрагировать эту логику в класс Singleton. Я не буду приводить еще один пример, но я надеюсь, что вы поняли идею. Пожалуйста, позвольте мне услышать ваши идеи для более элегантного способа выполнить то, что я пытаюсь сделать. Еще раз спасибо!

Ответы [ 19 ]

3 голосов
/ 06 октября 2010

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

2 голосов
/ 11 июня 2017

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

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

1 голос
/ 01 февраля 2017

Несмотря на то, что некоторые могут утверждать, вот синглтон с параметрами в конструкторе

public class Singleton {

    private static String aParameterStored;

    private static final Singleton instance = new Singleton("Param to set");

    private Singleton() {
        // do nothing
    }

    private Singleton(String param) {
        aParameterStored = param;
    }

    public static Singleton getInstance() {
        return instance;
    }

    /*
     * ... stuff you would like the singleton do
     */
}

Шаблон синглтона говорит:

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

, которые соблюдаются в этом примере.

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

1 голос
/ 04 декабря 2015

Не могли бы мы сделать что-то вроде этого:

public class Singleton {

    private int x;

    // Private constructor prevents instantiation from other classes
    private Singleton() {}

    /**
     * SingletonHolder is loaded on the first execution of Singleton.getInstance() 
     * or the first access to SingletonHolder.INSTANCE, not before.
     */
    private static class SingletonHolder { 
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance(int x) {
        Singleton instance = SingletonHolder.INSTANCE;
        instance.x = x;
        return instance;
    }
}
1 голос
/ 08 марта 2015

Если мы воспринимаем задачу как «как создать синглтон с состоянием», то нет необходимости передавать состояние в качестве параметра конструктора. Я согласен с постами, которые инициализируют состояния или используют метод set после получения экземпляра singleton.

Другой вопрос: хорошо ли иметь синглтон со штатом?

0 голосов
/ 27 июня 2009

Синглтон - это, конечно, «анти-паттерн» (при условии определения статики с переменным состоянием).

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

0 голосов
/ 29 ноября 2014

Это не совсем одно, но может быть что-то, что может решить вашу проблему.

public class KamilManager {

  private static KamilManager sharedInstance;

  /**
   * This method cannot be called before calling KamilManager constructor or else
   * it will bomb out.
   * @return
   */
  public static KamilManager getInstanceAfterInitialized() {
    if(sharedInstance == null)
        throw new RuntimeException("You must instantiate KamilManager once, before calling this method");

    return sharedInstance;
}

  public KamilManager(Context context, KamilConfig KamilConfig) {
    //Set whatever you need to set here then call:
  s  haredInstance = this;
  }
}
0 голосов
/ 29 марта 2018

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

public class MySingleton {

    private static volatile MySingleton INSTANCE;

    @SuppressWarnings("UnusedAssignment")
    public static void initialize(
            final SomeDependency someDependency) {

        MySingleton result = INSTANCE;

        if (result != null) {
            throw new IllegalStateException("The singleton has already "
                    + "been initialized.");
        }

        synchronized (MySingleton.class) {
            result = INSTANCE;

            if (result == null) {
                INSTANCE = result = new MySingleton(someDependency);
            } 
        }
    }

    public static MySingleton get() {
        MySingleton  result = INSTANCE;

        if (result == null) {
            throw new IllegalStateException("The singleton has not been "
                    + "initialized. You must call initialize(...) before "
                    + "calling get()");
        }

       return result;
    }

    ...
}
0 голосов
/ 27 июня 2009

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

Синглтон с аргументом в любом случае не имеет смысла - что бы произошло, если бы вы написали:

Singleton s = SingletonHolder.getInstance(1);
Singleton t = SingletonHolder.getInstance(2); //should probably throw IllegalStateException

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

...