Синглтон з аргументами на Яві


142

Я читав статтю Сінглтона у Вікіпедії і натрапив на такий приклад:

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

Дякую!


Редагувати: Я думаю, що я почав бурю суперечок із своїм бажанням використовувати 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).
    }
}

Як бачите, навіть тут у мене виникає проблема, що проходження іншого шляху до файлу нічого не означає після проходження першого. Ось чому мені подобається ідея про магазин, який розмістили у відповідях. У всякому разі, замість того, щоб включати логіку для завантаження файлу методом запуску, я хотів абстрагувати цю логіку класом Singleton. Я не приведу ще одного прикладу, але сподіваюся, що ви зрозумієте цю ідею. Будь ласка, дозвольте мені почути ваші ідеї для більш елегантного способу виконання того, що я намагаюся зробити. Ще раз дякую вам!


1
Заводська схема - це те, що ви хочете. В ідеалі завдання сітки повинні бути повністю незалежними від будь-якого іншого і надсилати всі дані, необхідні для виконання та повернення результатів. Однак це не завжди є найбільш можливим рішенням, тому серіалізація даних у файл не така вже й погана ідея. Я думаю, що вся одиночна річ - це трохи червона оселедець; ти не хочеш одинака.
oxbow_lakes

2
Досить прикро, що ви використовували термін Singleton, який постачається з таким багажем. Власний термін для цієї схеми - фактично інтернування. Інтернування - це спосіб гарантувати, що абстрактні значення представлені лише одним екземпляром. Струнне стажування - найпоширеніше: en.wikipedia.org/wiki/String_intern_pool.
notnoop

Ви можете поглянути на теракоту. Він підтримує ідентичність об'єкта через кластер. Коли ви надсилаєте посилання на дані, які вже є в кластері, вони не повторно серіалізуються.
Тейлор Готьє

21
Відкладаючи питання про те, чи слід коли-небудь використовувати шаблон однотонної, я зауважу, що майже кожна відповідь тут, мабуть, припускає, що метою надання аргументу є можливість створення "декількох однотонних", які відрізняються значенням вказаного параметра. Але інша можлива мета - забезпечити доступ до зовнішнього об'єкта, який є єдиним подібним об'єктом, який коли-небудь знадобиться унікальному екземпляру класу синглів . Отже, нам потрібно відрізняти параметр, передбачений для такого доступу, від параметра, призначеного для створення "декількох одиночних екземплярів".
Карл

2
Ще один сценарій для "синглтона з параметрами": веб-додаток, який побудує свій унікальний непорушний синглтон на основі інформації, що надходить із самим першим майбутнім запитом (потоком). Домен запиту може визначити поведінку деяких Сінглтон для прикладу
fustaki

Відповіді:


171

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

Однотонний, за визначенням, це об'єкт, на який ви бажаєте інстанціювати не більше одного разу. Якщо ви намагаєтесь подати параметри конструктору, в чому сенс синглтона?

У вас є два варіанти. Якщо ви хочете, щоб ваш одиночний ключ був ініціалізований деякими даними, ви можете завантажити його після отримання даних , наприклад:

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 #, я б просто використовувати властивості. Ява, напевно, так.
Зак

131
вибачте, це неправда. бувають ситуації, коли вам потрібно передати динамічно створені параметри, які залишаються однаковими для виконання програми для дірок. тому ви не можете використовувати константу в синглтоні, але повинні передати цю константу, коли вона створена. після того, як пройшов один раз свою ту саму константу за час дірки. сеттер не буде робити цю роботу, якщо вам потрібна конкретна константа в конструкторі.
masi

1
@masi, як каже автор, - це не сингл. Якщо вам потрібно пройти постійну динаміку, вам може знадобитися створити безліч таких класів з різними константами. Отже, в одиночному сенсі немає сенсу.
Дмитро Зайцев

53
Якщо вам потрібен лише один екземпляр класу протягом усього життя програми, але вам потрібно надати цьому екземпляру значення під час запуску, чому це більше не є однотонним?
Оскар

4
"Якщо ви намагаєтеся подати параметри конструктору, в чому сенс синглтона?" - Можна також сказати: "Якщо ви зробите всю свою програму єдиним екземпляром, у чому сенс аргументів командного рядка?", І відповідь полягає в тому, що це має багато сенсу. Тепер можна сказати, що це досить відрізняється від однокласного класу, за винятком випадків, якщо клас насправді є основним класом, який отримує аргументи [] від основного методу - тоді це навіть те саме. Заключний аргумент, який може просто стояти, полягає в тому, що це досить виняткова ситуація.
Президент 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- enums можна також вважати (або використовувати його) параметризованими одинаковими, хоча дозволяючи лише статичні варіанти фіксованого числа.

Однак якщо вам потрібен розподілений 1 розчин, розгляньте деякі рішення для кешування в бічній частині. Наприклад: EHCache, Terracotta тощо.

1 у сенсі охоплення декількох віртуальних машин, ймовірно, на декількох комп'ютерах.


Так, саме це мені і потрібно. Велике спасибі! Я погоджуюся, що те, як я обробляв аргументи на своєму прикладі, мало особливого сенсу, але я не думав про це. Дивіться моє пояснення у коментарях відповіді oxbow_lakes.

1
Це НЕ одинокий; тепер у вас є більше ніж один з них. LOL
oxbow_lakes

@Scott: Я б запропонував щось подібне до того, що запропонував Юваль нижче. Це має трохи більше сенсу, і у вас є справжній сингл. редагувати
Зак

Я сподіваюся, що ніхто не подумає мене редагувати імена в коді; Я можу уявити, що це справді заплутано для новачків. Відмова, якщо ви не погоджуєтесь
oxbow_lakes

Так, ми могли б назвати їх Мультитрон і все-таки досягти тієї ж мети, якою хотіла ОП в першу чергу ІМХО.
akarnokd

22

Ви можете додати налаштований метод ініціалізації, щоб відокремити інстанцію від отримання.

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

6

" Однотонний з параметрами не є однотонним " твердження не є абсолютно правильним . Нам потрібно проаналізувати це з точки зору програми, а не з точки зору коду.

Ми будуємо клас однотонних, щоб створити один екземпляр об'єкта за один запуск програми. Маючи конструктор з параметром, ви можете вбудовувати гнучкість у свій код, щоб змінювати деякі атрибути вашого одиночного об'єкта щоразу, коли ви запускаєте додаток. Це не є порушенням схеми Singleton. Це виглядає як порушення, якщо ви бачите це з точки зору коду.

Шаблони дизайну є, щоб допомогти нам писати гнучкий і розширюваний код, а не перешкоджати нам писати хороший код.


12
Це не відповідь на питання ОП, це має бути коментар.
Тьєррі Дж.

5

Використовуйте getters і setters, щоб встановити змінну і зробити конструктор за замовчуванням приватним. Потім використовуйте:

Singleton.getInstance().setX(value);

1
Не розумійте, чому це було знято з голосу. Це правдива відповідь. : /
Зак

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

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, ви використовуєте заводський шаблон.


3

У вашому прикладі ви не використовуєте синглтон. Зауважте, що якщо ви зробите наступне (якщо припустити, що Singleton.getInstance насправді статичний):

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

Тоді значення obj2.x дорівнює 3, а не 4. Якщо вам потрібно це зробити, зробіть це простим класом. Якщо кількість значень невелика і фіксована, ви можете розглянути можливість використання enum. Якщо у вас виникають проблеми з надмірною генерацією об'єктів (що зазвичай не так), ви можете розглянути значення кешування (і перевірити джерела або отримати допомогу з цим, оскільки очевидно, як створити кеші без небезпеки витоку пам'яті).

Ви також можете прочитати цю статтю, оскільки одиночні кнопки можна дуже легко використовувати.


3

Ще одна причина, по якій Singletons є антидіаграфом, полягає в тому, що якщо вони написані відповідно до рекомендацій, то з приватним конструктором їх дуже важко підклас і налаштувати для використання в певних тестових одиницях. Наприклад, знадобиться підтримка застарілого коду.


3

Якщо ви хочете створити клас Singleton, який виконує функції контексту, хорошим способом є створення конфігураційного файла та зчитування параметрів з файлу всередині instanca ().

Якщо параметри, що подають клас Singleton, отримуються динамічно під час роботи вашої програми, просто використовуйте статичний HashMap, що зберігає різні екземпляри у вашому класі Singleton, щоб переконатися, що для кожного параметра (s) створюється лише один екземпляр.


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

1

Якщо ми сприймемо проблему як "як зробити однотонний стан зі станом", тоді не потрібно передавати стан як параметр конструктора. Я погоджуюся з повідомленнями, які ініціалізують стани або використовують встановлений метод після отримання однотонного екземпляра.

Інше питання: чи добре мати одиночку зі штатом?


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
     */
}

Однотонний візерунок говорить:

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

які дотримуються цього прикладу.

Чому безпосередньо не встановити майно? Це випадок підручника, який показує, як ми можемо отримати сингл, що має конструктор з параметром, але це може бути корисно в деяких ситуаціях. Наприклад, у випадках успадкування, щоб змусити одиночну задати деякі властивості суперкласу.


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()кожен раз повертає той самий Екземпляр, я думаю, що це може спрацювати. Якщо це дуже неправильно, я його видалю, мене просто зацікавила ця тема.


-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ниткою (або синхронізуйте достатньо широко, або використовуйте перевірку того, що інша нитка не побила вас, або використовуйте якусь форму ф'ючерсів).


4
Тільки антидіаграма, якщо вона використовується неправильно, хоча це визначення анти-шаблону. Тільки те, що ви бачили їх там, де вони раніше не належали, не означає, що їм немає місця.
geowa4

Правильне використання синглтона - це демонстрація некомпетентного коду.
Том Хотін - тайклін

-6

Синглтонтони, як правило, вважаються анти-візерунками, і їх не слід використовувати. Вони не роблять код легким для тестування.

Синглтон з аргументом все одно не має сенсу - що буде, якби ви написали:

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

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


Це досить дискусійно.
AlbertoPL

1
Так, це дискусійно; звідси моє вживання слова "взагалі". Я думаю, що справедливо сказати, що вони, як правило, вважаються поганою ідеєю
oxbow_lakes

Це дискусійно - деякі люди стверджують, що те, що називається "анти-візерунками", відповідає визначенню шаблонів, це просто те, що вони погані.
Том Хотін - тайклін

Я розумію, що вони погані. Я займаюся розподіленими обчисленнями і потрібно ділити об'єкт серед кількох завдань. Замість того, щоб детерміновано ініціалізувати статичну змінну, я хотів би абстрагувати логіку в Singleton. Я думаю, що міг би зробити getInstance синхронізованим. Це би спрацювало? Що мені потрібно зробити - це завантажити файл один раз для багатьох завдань, і лише після того, як буде надіслано перше завдання. (Я не хочу, щоб мої дані були серіалізовані.) Я думав, що зроблю свій AbstractFileReader аргументом методу getInstance, щоб зробити Singleton більш гнучким. Я ціную ваш внесок.

Я думаю, ви можете неправильно зрозуміти, що означає "розподілений"? Є й інші способи досягти того, що ви хочете: чи розглядали ви ін'єкцію залежності? Або JNDI?
oxbow_lakes
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.