Singleton类设计 - null对象

时间:2010-11-05 21:28:22

标签: java multithreading singleton class-design

我和一位同事正在进行辩论:

我们有一个单例类,在我们的代码库中的多个位置使用。

最初,该类的设计方式使得您可以获取类对象,但该对象不会被完全初始化。

我的意思是:

mySingleton.getInstance().SomeMethod();

SomeMethod会导致错误,因为该类未初始化。为了让课程正常工作,必须要做到这一点:

mySingleton.getInstance().initObject();

mySingleton.getInstance().SomeMethod();

无论如何,我的争论是构造函数(使用第一个get实例调用)应该调用initObject,以便不会抛出任何错误。

您怎么看?

我的同事喜欢它,所以他知道课程何时初始化。 (即他在一个特定的代码行中调用initObject,并希望首先不需要它。

6 个答案:

答案 0 :(得分:8)

您更接近于使用Java实现单例模式的常规方式,而不是您的同事。请看一下Wikipedia。在那里,您将找到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;
    }
}

“Bill Pugh的解决方案”

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()方法,则可能存在多线程问题......

顺便说一下,我个人更喜欢使用“Bill Pugh”替代方案,但这3种方式都是有效的。

编辑在Esko评论之后,我添加了以下实现,在维基百科上不可用。我只想添加 1)单例实例不像上面的3个选项那样懒惰地创建; 2)因为它是一个枚举,所以你不能扩展任何类; 3)这非常非常奇怪。但它似乎在Java社区上大肆炒作,所以就在这里:

Enum方式

public enum Singleton {
    INSTANCE;
    Singleton() {
        /* Your init code goes here */
    }
}

答案 1 :(得分:1)

在我看来,使用initObject()方法是不必要的,似乎打败了拥有单身人士的目的。我觉得任何人使用你的单身人士的假设都会假设他们在调用getInstance()时会得到一个功能齐全的类实例。为什么要让某些人在他们甚至可以使用它之前调用initObject()来复杂化并增加代码混淆。为什么有人需要知道对象何时被初始化,是否应该假设检索到的对象已经初始化?

答案 2 :(得分:1)

我会在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();

答案 3 :(得分:0)

你的方法更接近单身人士的想法,首先是。

初始化内容对Singleton的用户应该是透明的。一个好方法是甚至不使用构造函数,而是将initObject代码放在静态块中, 在课程定义的开头。

答案 4 :(得分:0)

我不明白为什么你需要一个initObject方法。如果这是一个单例,那么该方法只会被调用一次。我只想在构造函数内部内联方法。

答案 5 :(得分:0)

如果可以避免,请不要使用initObject方法。

在两种情况下可能是不可避免的:

  • 单例的初始化在应用程序启动期间的某个特定点发生,这是强制这种情况发生的唯一合理方法。

  • 单例的初始化需要只能以这种方式提供的参数;例如从命令行参数构造的参数。

如果你必须有一个init方法,它应该抛出异常,如果它被调用两次。如果getInstance()方法被过早调用,它也可能是一个好主意。

这些应该适用于@ rsen娜答案中的“传统”范例。