在Java中实现单例设计模式的有效方法是什么?


当前回答

另一个经常用来反对单态的论点是它们的可测试性问题。出于测试目的,单体不容易被模拟。如果这是一个问题,我想做以下轻微修改:

public class SingletonImpl {

    private static SingletonImpl instance;

    public static SingletonImpl getInstance() {
        if (instance == null) {
            instance = new SingletonImpl();
        }
        return instance;
    }

    public static void setInstance(SingletonImpl impl) {
        instance = impl;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

添加的setInstance方法允许在测试期间设置单例类的实体模型实现:

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

这也适用于早期初始化方法:

public class SingletonImpl {

    private static final SingletonImpl instance = new SingletonImpl();

    private static SingletonImpl alt;

    public static void setInstance(SingletonImpl inst) {
        alt = inst;
    }

    public static SingletonImpl getInstance() {
        if (alt != null) {
            return alt;
        }
        return instance;
    }

    public void a() {
        System.out.println("Default Method");
    }
}

public class SingletonMock extends SingletonImpl {

    @Override
    public void a() {
        System.out.println("Mock Method");
    }

}

这有一个缺点,就是将此功能也暴露给普通应用程序。其他编写该代码的开发人员可能会尝试使用“setInstance”方法来更改特定函数,从而改变整个应用程序的行为,因此该方法的javadoc中至少应该包含一个良好的警告。

尽管如此,对于模型测试的可能性(当需要时),这种代码暴露可能是可以接受的代价。

其他回答

确保你真的需要它。在谷歌上搜索“singleton反模式”,看看反对它的一些论点。

我想这并没有什么本质上的问题,但它只是一种公开某些全局资源/数据的机制,因此请确保这是最好的方法。特别是,我发现依赖注入(DI)更有用,特别是如果您也在使用单元测试,因为DI允许您将模拟资源用于测试目的。

最简单的单例类:

public class Singleton {
  private static Singleton singleInstance = new Singleton();
  private Singleton() {}
  public static Singleton getSingleInstance() {
    return singleInstance;
  }
}

如果需要惰性地加载类的实例变量,则需要双重检查习惯用法。如果需要惰性地加载静态变量或单例,则需要按需初始化持有者习惯用法。

此外,如果单例需要可序列化,则所有其他字段都需要是暂时的,并且需要实现readResolve()方法以保持单例对象不变。否则,每次反序列化对象时,都会创建对象的新实例。readResolve()所做的是用readObject()替换读取的新对象,因为没有变量引用该对象,所以强制对该新对象进行垃圾回收。

public static final INSTANCE == ....
private Object readResolve() {
  return INSTANCE; // Original singleton instance.
} 

维基百科有一些单态的例子,也在Java中。Java5实现看起来非常完整,并且是线程安全的(应用了双重检查锁定)。

在写它之前,真的要考虑一下为什么你需要一个单例。关于使用它们有一个准宗教的争论,如果你在Java中搜索单例,你很容易就会发现。

就我个人而言,出于许多原因,我尽量避免单身汉,其中大部分可以通过谷歌搜索单身汉找到。我觉得单身汉经常被虐待,因为他们很容易被每个人理解。它们被用作将“全局”数据引入OO设计的机制,因为它很容易绕过对象生命周期管理(或者真正思考如何从B内部实现a)。看看像控制反转(IoC)或依赖注入(DI)这样的事情,可以找到一个不错的中间地带。

如果你真的需要一个,那么Wikipedia有一个很好的例子来说明单例的正确实现。