如何多次实例化单例模式?

8
我需要在我的代码中使用单例模式。我已经在Java中实现了它,并且它运行良好。我这样做的原因是确保在多个环境中,只有一个该类的实例。
但现在我想使用单元测试本地测试我的Singleton对象。为此,我需要模拟另一个该单例模式的实例(来自另一台设备)。所以,是否有可能为测试目的第二次实例化一个Singleton,或者我必须模拟它?
我不确定,但我认为可以通过使用不同的类加载器实现这种可能性。

5
嗯...这个名称似乎表明只应该有一个单独的实例。如果不能像这样进行测试,我建议类的设计可能存在缺陷。 - Fredrik Mörk
7
实例化单例两次不会导致测试失败吗?这是单例的一种明确定义的错误行为... - Piskvor left the building
1
这是许多原因之一,应该像瘟疫一样避免使用单例模式。 - ColinD
抱歉,我不小心进行了回滚。 - anon
为什么你的测试需要实例化第二个单例? - NotMe
模拟从另一个设备发送的相同对象。 - anon
9个回答

18

传统上,一个单例模式会创建自己的实例,并且仅会创建一次。在这种情况下,不可能创建第二个实例。

如果你使用依赖注入,你可以让框架为你创建单例。单例不能防止其他实例的创建(即它具有公共构造函数),但是依赖注入框架只实例化一个实例。在这种情况下,你可以创建更多的实例进行测试,而你的对象不会被单例代码所混淆。


17

单例模式的目的在于只能实例化一次。


是的,这就是我使用它的原因。但我认为总有办法可以做到。 - RoflcoptrException
3
@Sebi - 然后用它进行测试。或者使用模拟测试,在没有它的情况下进行测试,但不要试图使它成为它不是的东西。 - Oded

9
您可以使用反射调用单例类的私有构造函数来创建该类的新实例。
class MySingleton {
    private MySingleton() {
    }
}

class Test {
    public void test() throws Exception {
        Constructor<MySingleton> constructor = MySingleton.class.getConstructor();
        constructor.setAccessible(true);
        MySingleton otherSingleton = constructor.newInstance();
    }
}

当然,你也可以通过子类化MySingleton来公开构造函数。如果您有调用MySingleton.getInstance().blach()的现有代码,则所有这些代码都将失败。 - Justin
这是为了测试单例模式而进行的。我不建议在非测试代码中这样做。我认为可以使用反射来访问无法访问的方法,以便进行测试。 - Abhinav Sarkar
我认为最好的方法是修复代码,而不是通过反射来绕过它。 - Tom Hawtin - tackline
为什么我在Java6中会收到“java.lang.NoSuchMethodException”的错误?这个问题已经修复了吗? - Vamsi Nerella

6

单例模式的定义是只能实例化一次。然而,如果您的单元测试需要两个单例,这强烈表明您的对象不应该真正成为单例,您应该重新考虑单例模式的设计。


或者相反,测试本身是谨慎的。 - NotMe
在这种情况下,创建一个resetInstance方法可能会有所帮助,该方法将删除类内部的私有实例 - 这将有助于测试这种情况。 - kirugan

3

3
首先,为什么需要创建一个新的单例来运行单元测试?单元测试不应该与正常应用程序并发运行,因此您应该能够访问原始单例而不必担心修改它。
您需要明确第二个单例的特定原因吗?

2

你可以创建另一个静态方法getInstance2,代码如下:

class MySingleton
{
    private MySingleton(){}
    private static MySingleton instance1 = new MySingleton();
    private static MySingleton instance2 = new MySingleton();

    public static MySingleton getInstance(){ return instance1; }
    public static MySingleton getInstance2(){ return instance2; }
}

嗯,是的,但那样我就得改代码了。如果可能的话,我想避免这种情况。 - RoflcoptrException
猜测这是毁掉你的代码的好方法。但好处是,你后面的单元测试会更频繁地触发异常 :-) - Eiko

0

你可以在地图上保留一个键并用键填充实例

public class MultiSingleton {
/**** Non-static Global Variables ***/
String status = "";
private BaseSmartCard bsc;
/***********************************/
private static Object lockObject = new Object();
private String serialNo;

private static Map<String, MultiSingleton> mappedObjects = new TreeMap<String, MultiSingleton>();

protected MultiSingleton() {

}


public static MultiSingleton getInstance(String serialNo,long slotNo){
    if (mappedObjects.isEmpty() || !mappedObjects.containsKey(serialNo)) {
        MultiSingleton instance = new MultiSingleton();
        instance.setSerialNo(serialNo);
        mappedObjects.put(serialNo, instance);
        return instance;
    } else if (mappedObjects.containsKey(serialNo)) {
        return mappedObjects.get(serialNo);
    }else {
        return null;
    }
}

0

Singleton ston=Singleton.getInstance();将返回单例对象。通过使用“ston”对象,如果我们调用写在Singleton类中的方法createNewSingleTonInstance(),将会产生新的实例。

public class Singleton {

private String userName;
private String Password;
private static Singleton firstInstance=null;
private Singleton(){}


public static synchronized Singleton getInstance(){
    if(firstInstance==null){
        firstInstance=new Singleton();
        firstInstance.setUserName("Prathap");
        firstInstance.setPassword("Mandya");
    }
    return firstInstance;
}


public void setUserName(String userName) {
    this.userName = userName;
}
public String getUserName() {
    return userName;
}
public void setPassword(String password) {
    Password = password;
}
public String getPassword() {
    return Password;
}

public Singleton createNewSingleTonInstance(){
    Singleton s=new Singleton();
    s.setUserName("ASDF");
    s.setPassword("QWER");
    return s;
}
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接