Синглтон с аргументами на Java

146

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

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. Я не буду приводить еще один пример, но надеюсь, что вы уловили идею. Пожалуйста, позвольте мне услышать ваши идеи о более элегантном способе выполнения того, что я пытаюсь сделать. Еще раз спасибо!


источник
1
Заводской узор - это то, что вам нужно. В идеале сеточные задачи должны быть полностью независимыми от чего-либо еще и получать все данные, необходимые для выполнения и возврата результатов. Однако это не всегда наиболее приемлемое решение, поэтому сериализация данных в файл - не такая уж плохая идея. Я думаю, что вся эта синглтон-идея - отвлекающий маневр; вам не нужен синглтон.
oxbow_lakes
2
Очень жаль, что вы использовали термин Синглтон, связанный с таким багажом. Правильный термин для этого шаблона - интернирование. Интернирование - это метод, гарантирующий, что абстрактные значения представлены только одним экземпляром. Наиболее распространенное использование - интернирование строк: en.wikipedia.org/wiki/String_intern_pool.
notnoop
Вы можете взглянуть на Терракоту. Он поддерживает идентичность объекта в кластере. Когда вы отправляете ссылку на данные, уже находящиеся в кластере, она не сериализуется повторно.
Тейлор Готье
21
Отложив в сторону вопрос о том, следует ли когда-либо использовать шаблон синглтона, я бы отметил, что почти каждый ответ здесь, похоже, предполагает, что цель предоставления аргумента состоит в том, чтобы разрешить создание «нескольких синглтонов», которые различаются значением указанного параметра. Но другая возможная цель - предоставить доступ к внешнему объекту, который является единственным в своем роде объектом, который когда-либо понадобится уникальному экземпляру одноэлементного класса . Поэтому нам нужно отличать параметр, предоставляемый для такого доступа, от параметра, предназначенного для создания «нескольких экземпляров синглтона».
Carl
2
Другой сценарий для «синглтона с параметрами»: веб-приложение, которое построит свой уникальный неизменяемый синглтон на основе информации, поступающей с самым первым предстоящим запросом (потоком). Например, домен запроса может определять поведение какого-то синглтона
фустаки

Ответы:

173

Я проясню свою точку зрения: синглтон с параметрами - это не синглтон .

Синглтон по определению - это объект, для которого требуется создать экземпляр не более одного раза. Если вы пытаетесь передать параметры конструктору, в чем смысл синглтона?

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

SingletonObj singleton = SingletonObj.getInstance();
singleton.init(paramA, paramB); // init the object with data

Если операция, которую выполняет ваш синглтон, повторяется и каждый раз с разными параметрами, вы также можете передать параметры выполняемому основному методу:

SingletonObj singleton = SingletonObj.getInstance();
singleton.doSomething(paramA, paramB); // pass parameters on execution

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

Юваль Адам
источник
1
+1 Я бы, наверное, так и поступил при кодировании. В C # я бы просто использовал свойства. Ява, наверное, вот так.
Zack
133
извините, это неправда. бывают ситуации, когда вам необходимо передать динамически созданные параметры, которые остаются неизменными для времени выполнения приложения дыры. поэтому вы не можете использовать константу в синглтоне, но должны передать эту константу при ее создании. после того, как прошел один раз, это та же константа для времени отверстия. сеттер не будет выполнять эту работу, если вам нужна эта конкретная константа в конструкторе.
omni
54
Если вам нужен только один экземпляр класса на все время существования приложения, но вам нужно предоставить этому экземпляру значение во время запуска, почему это больше не синглтон?
Оскар
2
Примером против вашего предположения является вспомогательный класс базы данных в android. Лучшей практикой было бы иметь синглтон для этого класса, чтобы поддерживать только одно соединение с базой данных, но он ожидает для него параметр ( Context).
Аман Дип Гаутам,
4
"Если вы пытаетесь передать параметры конструктору, в чем смысл синглтона?" - Можно также сказать: «Если вы сделаете все свое приложение единым экземпляром, в чем смысл аргументов командной строки?», И ответ будет заключаться в том, что это имеет большой смысл. Теперь можно сказать, что это довольно сильно отличается от одноэлементного класса, за исключением случаев, когда класс на самом деле является основным классом, который получает args [] от основного метода - тогда это даже то же самое. Последний аргумент, который может остаться в силе, заключается в том, что это довольно исключительная ситуация.
Президент Dreamspace
41

Я думаю, вам нужно что-то вроде фабрики для создания и повторного использования объектов с различными параметрами. Это может быть реализовано с помощью синхронизированного HashMapили ConcurrentHashMapсопоставленного параметра ( Integerнапример) с параметризуемым классом «singleton».

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

Вот пример такого магазина:

public final class UsefulObjFactory {

    private static Map<Integer, UsefulObj> store =
        new HashMap<Integer, UsefulObj>();

    public static final class UsefulObj {
        private UsefulObj(int parameter) {
            // init
        }
        public void someUsefulMethod() {
            // some useful operation
        }
    }

    public static UsefulObj get(int parameter) {
        synchronized (store) {
            UsefulObj result = store.get(parameter);
            if (result == null) {
                result = new UsefulObj(parameter);
                store.put(parameter, result);
            }
            return result;
        }
    }
}

Чтобы продвинуть его еще дальше, Java enumможно также рассматривать (или использовать как) параметризованные синглтоны, хотя допускает только статические варианты с фиксированным числом.

Однако, если вам нужно распределенное решение 1 , подумайте о решении для бокового кэширования. Например: EHCache, Terracotta и т. Д.

1 в смысле охвата нескольких виртуальных машин на нескольких компьютерах.

акарнокд
источник
Да, это именно то, что мне нужно. Большое спасибо! Я согласен с тем, что то, как я обрабатывал аргументы в моем примере, не имело особого смысла, но я не думал об этом. См. Мое объяснение в комментариях к ответу oxbow_lakes.
1
Это НЕ синглтон; теперь у вас их несколько. LOL
oxbow_lakes
@Scott: Я бы предложил что-то вроде того, что предложил Юваль ниже. Это имеет немного больше смысла, и у вас есть «настоящий» синглтон. edit
Zack
Я надеюсь, что никто не против того, чтобы я редактировал имена в коде; Я могу представить, что это действительно сбивает с толку новичков. Откатывайтесь, если вы не согласны
oxbow_lakes
Да, мы могли бы назвать их Multitron и при этом достичь той же цели, которую OP хотел в первую очередь ИМХО.
akarnokd
25

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

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

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

    public static Singleton getInstance() {
        if(singleton == null) {
            throw new AssertionError("You have to call init first");
        }

        return singleton;
    }

    public synchronized static Singleton init(int x) {
        if (singleton != null)
        {
            // in my opinion this is optional, but for the purists it ensures
            // that you only ever get the same instance when you call getInstance
            throw new AssertionError("You already initialized me");
        }

        singleton = new Singleton(x);
        return singleton;
    }

}

Затем вы можете позвонить Singleton.init(123)один раз, чтобы настроить его, например, при запуске приложения.

Мигель
источник
13

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

    public enum EnumSingleton {

    INSTANCE;

    private String name; // Mandatory
    private Double age = null; // Not Mandatory

    private void build(SingletonBuilder builder) {
        this.name = builder.name;
        this.age = builder.age;
    }

    // Static getter
    public static EnumSingleton getSingleton() {
        return INSTANCE;
    }

    public void print() {
        System.out.println("Name "+name + ", age: "+age);
    }


    public static class SingletonBuilder {

        private final String name; // Mandatory
        private Double age = null; // Not Mandatory

        private SingletonBuilder(){
          name = null;
        }

        SingletonBuilder(String name) {
            this.name = name;
        }

        public SingletonBuilder age(double age) {
            this.age = age;
            return this;
        }

        public void build(){
            EnumSingleton.INSTANCE.build(this);
        }

    }


}

Затем вы можете создать / создать / параметризовать его следующим образом:

public static void main(String[] args) {
    new EnumSingleton.SingletonBuilder("nico").age(41).build();
    EnumSingleton.getSingleton().print();
}
Gerardnico
источник
6

Утверждение « Синглтон с параметрами не является одноэлементным » не совсем корректно . Нам нужно проанализировать это с точки зрения приложения, а не с точки зрения кода.

Мы создаем одноэлементный класс для создания одного экземпляра объекта за один запуск приложения. Имея конструктор с параметром, вы можете повысить гибкость своего кода, чтобы изменять некоторые атрибуты вашего одноэлементного объекта каждый раз, когда вы запускаете приложение. Это не нарушение паттерна Singleton. Если посмотреть на это с точки зрения кода, это выглядит как нарушение.

Шаблоны проектирования помогают нам писать гибкий и расширяемый код, а не мешают нам писать хороший код.

Винод Налла
источник
12
Это не ответ на вопрос OP, это должен быть комментарий.
Тьерри Дж.
5

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

Singleton.getInstance().setX(value);
АльбертоПЛ
источник
1
Не понимаю, почему это было отвергнуто ... Это правильный ответ, tbh. : /
Zack
13
Потому что это чушь ответ. Например, представьте систему, в которой начальное имя пользователя и пароль для начального администратора являются аргументами конструктора. Теперь, если я сделаю это синглтоном и сделаю, как вы говорите, я получу геттеры и сеттеры для администратора, а это не совсем то, что вы хотите. Таким образом, хотя ваш вариант может быть действительным в некоторых случаях, на самом деле он не отвечает на общий случай, о котором шла речь. (да, я работаю над системой, которую я описал, и нет, я бы не использовал одноэлементный шаблон, если бы не тот факт, что в задании сказано «используйте одноэлементный шаблон здесь»)
Джаспер
5

Удивлен, что никто не упомянул, как создается / извлекается регистратор. Например, ниже показано, как извлекается регистратор Log4J .

// Retrieve a logger named according to the value of the name parameter. If the named logger already exists, then the existing instance will be returned. Otherwise, a new instance is created.
public static Logger getLogger(String name)

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

69   Hashtable ht;
...
258  public
259  Logger getLogger(String name, LoggerFactory factory) {
260    //System.out.println("getInstance("+name+") called.");
261    CategoryKey key = new CategoryKey(name);
262    // Synchronize to prevent write conflicts. Read conflicts (in
263    // getChainedLevel method) are possible only if variable
264    // assignments are non-atomic.
265    Logger logger;
266
267    synchronized(ht) {
268      Object o = ht.get(key);
269      if(o == null) {
270        logger = factory.makeNewLoggerInstance(name);
271        logger.setHierarchy(this);
272        ht.put(key, logger);
273        updateParents(logger);
274        return logger;
275      } else if(o instanceof Logger) {
276        return (Logger) o;
277      } 
...
Wanghq
источник
4

Модификация паттерна Singleton, использующая идиому Билла Пью "инициализация по требованию" . Это потокобезопасный вариант без дополнительных затрат на специализированные языковые конструкции (например, изменчивые или синхронизированные):

public final class RInterfaceHL {

    /**
     * Private constructor prevents instantiation from other classes.
     */
    private RInterfaceHL() { }

    /**
     * R REPL (read-evaluate-parse loop) handler.
     */
    private static RMainLoopCallbacks rloopHandler = null;

    /**
     * SingletonHolder is loaded, and the static initializer executed, 
     * on the first execution of Singleton.getInstance() or the first 
     * access to SingletonHolder.INSTANCE, not before.
     */
    private static final class SingletonHolder {

        /**
         * Singleton instance, with static initializer.
         */
        private static final RInterfaceHL INSTANCE = initRInterfaceHL();

        /**
         * Initialize RInterfaceHL singleton instance using rLoopHandler from
         * outer class.
         * 
         * @return RInterfaceHL instance
         */
        private static RInterfaceHL initRInterfaceHL() {
            try {
                return new RInterfaceHL(rloopHandler);
            } catch (REngineException e) {
                // a static initializer cannot throw exceptions
                // but it can throw an ExceptionInInitializerError
                throw new ExceptionInInitializerError(e);
            }
        }

        /**
         * Prevent instantiation.
         */
        private SingletonHolder() {
        }

        /**
         * Get singleton RInterfaceHL.
         * 
         * @return RInterfaceHL singleton.
         */
        public static RInterfaceHL getInstance() {
            return SingletonHolder.INSTANCE;
        }

    }

    /**
     * Return the singleton instance of RInterfaceHL. Only the first call to
     * this will establish the rloopHandler.
     * 
     * @param rloopHandler
     *            R REPL handler supplied by client.
     * @return RInterfaceHL singleton instance
     * @throws REngineException
     *             if REngine cannot be created
     */
    public static RInterfaceHL getInstance(RMainLoopCallbacks rloopHandler)
            throws REngineException {
        RInterfaceHL.rloopHandler = rloopHandler;

        RInterfaceHL instance = null;

        try {
            instance = SingletonHolder.getInstance();
        } catch (ExceptionInInitializerError e) {

            // rethrow exception that occurred in the initializer
            // so our caller can deal with it
            Throwable exceptionInInit = e.getCause();
            throw new REngineException(null, exceptionInInit.getMessage());
        }

        return instance;
    }

    /**
     * org.rosuda.REngine.REngine high level R interface.
     */
    private REngine rosudaEngine = null;

    /**
     * Construct new RInterfaceHL. Only ever gets called once by
     * {@link SingletonHolder.initRInterfaceHL}.
     * 
     * @param rloopHandler
     *            R REPL handler supplied by client.
     * @throws REngineException
     *             if R cannot be loaded.
     */
    private RInterfaceHL(RMainLoopCallbacks rloopHandler)
            throws REngineException {

        // tell Rengine code not to die if it can't
        // load the JRI native DLLs. This allows
        // us to catch the UnsatisfiedLinkError
        // ourselves
        System.setProperty("jri.ignore.ule", "yes");

        rosudaEngine = new JRIEngine(new String[] { "--no-save" }, rloopHandler);
    }
}
Текумара
источник
Я думаю , что это было бы хорошей идеей finally { RInterfaceHL.rloopHandler = null; }в getInstance, потому что статическая ссылка может привести к утечке памяти , если мы не будем осторожны. В вашем случае похоже, что это не проблема, но я могу представить сценарий, в котором переданный объект большой и используется RInterfaceHLctor только для получения некоторых значений, а не для сохранения ссылки на него.
TWiStErRob
Идея: return SingletonHolder.INSTANCEтак же отлично подойдёт и в getInstance. Я не думаю, что здесь нужна инкапсуляция, потому что внешний класс уже знает внутренности внутреннего класса, они тесно связаны: он знает, что rloopHandlerперед вызовом нужен init. Кроме того, закрытый конструктор не имеет никакого эффекта, потому что закрытые данные внутреннего класса просто доступны внешнему классу.
TWiStErRob
1
Ссылка не работает. Вы имели в виду en.wikipedia.org/wiki/Initialization-on-demand_holder_idiom ?
Хорхе Лавин
3

Причина, по которой вы не можете понять, как выполнить то, что пытаетесь сделать, вероятно, состоит в том, что то, что вы пытаетесь сделать, на самом деле не имеет смысла. Вы хотите вызывать getInstance(x)с разными аргументами, но всегда возвращать один и тот же объект? Какого поведения вы хотите, когда звоните, getInstance(2)а затем getInstance(5)?

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

С другой стороны, если вы хотите getInstance(2)и getInstance(5)возвращать разные объекты, вы не используете шаблон Singleton, вы используете шаблон Factory.

хаос
источник
3

В вашем примере вы не используете синглтон. Обратите внимание, что если вы сделаете следующее (при условии, что Singleton.getInstance действительно статический):

Singleton obj1 = Singleton.getInstance(3);
Singleton obj2 = Singleton.getInstance(4);

Тогда значение obj2.x равно 3, а не 4. Если вам нужно это сделать, сделайте его простым классом. Если количество значений небольшое и фиксированное, вы можете рассмотреть возможность использования файла enum. Если у вас возникла проблема с чрезмерным генерированием объектов (что обычно не так), вы можете рассмотреть возможность кэширования значений (и проверить источники или получить помощь с этим, поскольку очевидно, как создавать кеши без опасности утечек памяти).

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

Кэти Ван Стоун
источник
3

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

JosefB
источник
3

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

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

user3025839
источник
1

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

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;
  }
}
Камильский81
источник
1

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

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

user3014901
источник
1

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

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

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

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 говорит:

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

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

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

Zou
источник
0

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

public class example  {
    private volatile static example instance;

    private String string;
    private int iInt = -1; //any number you know you don't want to use here

  private example() {

    //In case someone uses the private method to create a new Instance
    if (instance != null){
      throw new RuntimeException("Use getInstance() method to get the single instance of this class.");
    }
  }

  public synchronized static example getIsntance(){
    if(instance == null){
      instance = new example();
    }
    return instance;
  }

public void methodDoingWork(){
    if(checkInit()){
      //DoSome
    }
  }

  private boolean checkInit(){
    boolean filled = (this.string != null) && (this.iInt != -1);
    return filled;
  }

  public void setString(String string) {
    if(this.string == null){
      this.string = string;
    }else{
      throw new RuntimeException("You try to override an already setValue"); 
    }
  }

  public void setiInt(int iInt) {
    if(this.iInt == -1){
      this.iInt = iInt;
    }else{
      throw new RuntimeException("You try to override an already setValue");
    }
  }
}

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

HydroHeiperGen
источник
-1

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

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;
    }

    ...
}
Майкл Эндрюс
источник
Я полагаю, всегда мог вернуть «результат» в методе инициализации.
Майкл Эндрюс
-2

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

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

Том Хотин - tackline
источник
4
Только анти-шаблон, если он используется неправильно, хотя это определение анти-шаблона. То, что вы видели их там, где им не место в прошлом, не означает, что им нет места.
geowa4
Правильное использование синглтона - демонстрация некомпетентного кода.
Tom Hawtin - tackline
-6

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

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

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

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

Oxbow_lakes
источник
Это довольно спорно.
AlbertoPL
1
Да , это спорно; отсюда и употребление мной слова «в целом». Я думаю, будет справедливо сказать, что их вообще считают плохой идеей
oxbow_lakes
Это спорно - некоторые люди утверждают, что называют «анти-паттерны» подпадают под определение шаблонов, это просто , что они плохие модели.
Tom Hawtin - tackline
Я понимаю, что они плохие. Я занимаюсь распределенными вычислениями, и мне нужно разделить объект между несколькими задачами. Вместо того, чтобы детерминированно инициализировать статическую переменную, я хотел бы абстрагировать логику в синглтоне. Думаю, я мог бы синхронизировать getInstance. Это сработает? Что мне нужно сделать, так это загрузить файл один раз для многих задач и только после того, как первая задача будет отправлена. (Я не хочу, чтобы мои данные были сериализованы.) Я подумал, что сделаю свой AbstractFileReader аргументом метода getInstance, чтобы сделать синглтон более гибким. Я ценю ваш вклад.
Я думаю, вы можете неправильно понять, что означает «распределенный»? Есть и другие способы достижения желаемого: рассматривали ли вы внедрение зависимостей? Или JNDI?
oxbow_lakes