Дизайн класса Singleton - нулевой объект - PullRequest
5 голосов
/ 06 ноября 2010

Я и коллега спорим:

У нас есть одноэлементный класс, который используется в нескольких местах в нашей кодовой базе.

Изначально класс был разработан таким образом, чтобы вы могли получить объект класса, но этот объект не был бы полностью инициализирован.

Под этим я подразумеваю:

mySingleton.getInstance().SomeMethod();

SomeMethod приведет к ошибкам, поскольку класс не был инициализирован. Чтобы класс работал правильно, это должно произойти:

mySingleton.getInstance().initObject();

mySingleton.getInstance().SomeMethod();

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

Что вы думаете?

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

Ответы [ 6 ]

8 голосов
/ 06 ноября 2010

Вы ближе к обычному способу реализации одноэлементного шаблона в Java, чем ваш коллега. Пожалуйста, взгляните на Википедию . Там вы найдете 3 наиболее распространенных реализации Java:

Традиционный простой способ

public class Singleton {
    private static final Singleton INSTANCE = new Singleton();

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

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

«Решение Билла Пью»

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 { 
        public static final Singleton INSTANCE = new Singleton();
    }

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

Традиционный простой способ с использованием синхронизации

public class Singleton {
    // volatile is needed so that multiple threads can reconcile the instance
    // semantics for volatile changed in Java 5.
    private volatile static Singleton singleton;

    private Singleton() {}

    // synchronized keyword has been removed from here
    public static Singleton getSingleton() {
        // needed because once there is singleton available no need to acquire
        // monitor again & again as it is costly
        if (singleton == null) {
            synchronized (Singleton.class) {
                // this is needed if two threads are waiting at the monitor at the
                // time when singleton was getting instantiated
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

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

Кстати, лично я предпочитаю использовать альтернативу "Билла Пью", но 3 способа действительны.

Редактировать После любезного комментария Esko я добавляю следующую реализацию, , которая недоступна в Википедии . Я просто хотел бы добавить, что 1) Экземпляр синглтона не создается лениво, как 3 варианта выше; 2) Поскольку это enum, вы не можете расширять любой класс; и 3) Это очень, очень странно. Но, похоже, это довольно распространено в сообществе Java, поэтому оно здесь:

Enum way

public enum Singleton {
    INSTANCE;
    Singleton() {
        /* Your init code goes here */
    }
}
1 голос
/ 06 ноября 2010

Я бы поставил проверку инициализации в методе getInstance ():

public static MySingleton getInstance() {
  if (! isInitialized)
    initialize();
  return instance;
}

... и сделать метод initialize () закрытым.

РЕДАКТИРОВАТЬ : Очевидно, я неправильно понял вопрос. Основная проблема заключается в том, должен ли initObject () быть внутри или снаружи конструктора. Правильный ответ может зависеть от реальной проблемы, но я думаю, что в общем случае он должен быть внутри конструктора, потому что если единственная цель помещения метода initObject () вне конструктора - сообщить всем, где происходит инициализация, вы также можете написать комментарий:

// Attention everyone!! Object is initialized here!!1!11!
MySingleton instance = MySingleton.getInstance();

// Object is NOT initialized here!!11!1!!
MySingleton instance2 = MySingleton.getInstance();
1 голос
/ 06 ноября 2010

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

0 голосов
/ 06 ноября 2010

Если вы можете избежать этого, не используйте метод initObject.

Это может быть неизбежно в двух ситуациях:

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

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

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

Это должно быть применено к "традиционному" примеру в ответе @ rsenna.

0 голосов
/ 06 ноября 2010

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

0 голосов
/ 06 ноября 2010

Ваш подход ближе к идее Singleton.

Инициализация должна быть прозрачной для пользователя (ей) Singleton.Хороший способ сделать это - даже не использовать конструктор, а поместить код initObject в статический блок в начале определения класса.

...