2010-06-17 21 views
7

Potrzebuję singleton w moim kodzie. Zaimplementowałem go w Javie i działa dobrze. Powodem, dla którego to zrobiłem, jest zapewnienie, że w środowisku mnogim istnieje tylko jedno wystąpienie tej klasy.Jak utworzyć instancję Singleton wiele razy?

Ale teraz chcę przetestować mój obiekt Singleton lokalnie za pomocą testu Jednostki. Z tego powodu muszę zasymulować inną instancję tego Singleton (obiekt, który pochodziłby z innego urządzenia). Czy istnieje możliwość utworzenia Singletonu po raz drugi w celach testowych lub czy muszę go wyśmiać?

Nie jestem pewien, ale myślę, że byłoby to możliwe przy użyciu innego programu ładującego klasy?

+4

Um .. nazwa rodzaju wskazuje, że musi istnieć tylko jedna instancja. Jeśli nie można go przetestować jako takiego, sugeruję, że może wystąpić usterka w projekcie klasy. –

+6

Czy utworzenie wystąpienia Singletona dwukrotnie nie byłoby porażką testową? Jest to dobrze zdefiniowane złe zachowanie dla singletona ... – Piskvor

+1

Jest to jeden z wielu powodów, dla których Singletonów należy unikać, tak jak zarazy. – ColinD

Odpowiedz

9

Możesz przywołać prywatnego konstruktora klasy singleton za pomocą refleksji, aby utworzyć nową instancję klasy.

class MySingleton { 
    private MySingleton() { 
    } 
} 

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

Oczywiście, lub możesz po prostu podklasować MySingleton, aby odsłonić konstruktora. Jeśli masz istniejący kod wywołujący MySingleton.getInstance(). Blach(), ten cały kod zakończy się niepowodzeniem. – Justin

+0

dziękuję to właśnie szukam – RoflcoptrException

+0

To jest w celu testowania klasy singleton. Nie polecam robić tego w kodzie beztestowym. Myślę, że dobrze jest użyć refleksji, aby dotrzeć do metod niedostępnych w inny sposób do celów testowych. –

16

Punktem Singleton jest to, że można go utworzyć tylko raz.

+0

tak, dlatego go użyłem. Ale myślałem, że jakoś można to zrobić. – RoflcoptrException

+3

@Sebi - następnie przetestuj za jego pomocą. Możesz też użyć mocku, aby go przetestować bez niego, ale nie próbuj sprawić, by był czymś, czym nie jest. – Oded

+0

Dlaczego upadek? – Oded

1

można po prostu dokonać innego statyczną metodę getInstance2, który wygląda tak:

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

hmm tak, ale potem musiałbym zmienić kod. Jeśli to możliwe, chcę tego uniknąć. – RoflcoptrException

+0

Zgadnij, to jest dobry sposób na zepsucie kodu. Z drugiej strony, twoje późniejsze testy jednostek będą generować znacznie częściej wyjątki :-) – Eiko

17

Tradycyjnie Singleton tworzy swoją własną instancję, a tworzy go tylko raz. W takim przypadku nie można utworzyć drugiej instancji.

Jeśli używasz Dependency Injection, możesz pozwolić frameworkowi stworzyć singleton. Singleton nie chroni przed innymi instancjami (tj. Ma publiczny konstruktor), ale framework zastrzyku zależności tworzy tylko jedno wystąpienie. W takim przypadku można utworzyć więcej instancji do testowania, a obiekt nie jest zaśmiecony kodem singletowym.

6

Singleton z definicji można utworzyć tylko raz. Jednak fakt, że twój test jednostkowy wymagadwóch pojedynczych jednostek, jest silnym dowodem, że twój obiekt nie powinien być singletonem i powinieneś przemyśleć projekt singletonowy.

+0

Albo, przeciwnie, że sam test jest ostrożny. – NotMe

+0

W takim przypadku pomocne może być utworzenie metody "resetInstance", która usunie prywatne wystąpienie wewnątrz klasy - pomoże to przetestować takie przypadki. – kirugan

2

Po pierwsze, dlaczego trzeba utworzyć nowy singleton, aby przeprowadzić test jednostkowy? Test jednostkowy nie powinien działać równolegle z normalną aplikacją, więc powinieneś być w stanie uzyskać dostęp do oryginalnego singletonu bez obawy o jego modyfikację.

Czy istnieje szczególny powód, dla którego potrzebujesz wyraźnego drugiego singletonu?

0

Singleton ston=Singleton.getInstance(); zwróci obiekt singleton. Korzystając z obiektu "ston", jeśli nazwiemy metodę createNewSingleTonInstance(), która jest napisana w klasie Singleton, poda nową instancję.

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

można trzymać klawisz na mapie i wypełnić wystąpienie z kluczem

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; 
    } 
} 
Powiązane problemy