2013-02-19 14 views
5

Zajmuję się tworzeniem aplikacji do wizji komputerowej i będę potrzebować klasy Classifier. Ta klasa będzie niezmienna dla każdego uruchomienia aplikacji i ładuje wyszkolone dane z dysku podczas inicjalizacji. Chcę zapewnić, że cały program będzie miał dostęp do tych samych wyszkolonych danych i chcę zablokować ponowne ładowanie z dysku po ich załadowaniu.Klasa Singleton z parametrem

To, co rozważałem, to używać klasy statycznej lub pojedynczej. Nie wiem, jak załadować dane do klasy statycznej, ponieważ ścieżka do pliku danych nie jest znana podczas kompilacji - będzie to argument programowy. Tak więc myślałem o wzorze Singleton, ale nie wiem, jak zainicjować go dynamicznie.

Mój pomysł polegał na użyciu następujących:

class Singleton { 
    private static Singleton instance; 
    private Singleton() { ... } 
    private static SomeDataObject data; 

    public static Singleton getInstance() { 
     if(instance == null) 
      instance = new Singleton(); 

      return instance; 
    } 

    public static init(string dataPath){ 
     if(data == null) 
      loadDataFromFile(dataPath) 
    } 
} 

To nie działać, bo nie mają kontroli, która zostanie wywołana metoda pierwsza.

Wiem, że właściwym sposobem byłoby stworzenie instancji z danymi na początku i przekazanie jej wszystkim klasom i metodom, które jej potrzebują, ale to nie jest ogólne rozwiązanie. Mogę śledzić wszystkie połączenia do Classifier w moim własnym kodzie, ale jeśli zrobiłbym kod jako API, byłby to problem.

W skrócie, jak zainicjować singleton w czasie wykonywania?

+0

W jaki sposób programowo dostępny jest parametr 'dataPath'? –

+0

Nie trzeba *** *** obiektu statycznego singleton *** i obiektu danych ***. Ponadto twoja obecna implementacja singleton nie jest bezpieczna dla wątków. – Perception

+0

@MattBall może to być albo z GUI, albo z argumentu wiersza poleceń – jnovacho

Odpowiedz

8

Nie sądzę (dokładnie), co chcesz zrobić, będzie działać.

poniżej będzie działać:

public static void main(String[] args) 
{ 
    Singleton.init("somepath"); 
    ... 
    Singleton.getInstance().doingStuff(); 
    ... 
} 

Lepsze wdrożenie może być: (co spowodowałoby NullPointerException jeśli starają się używać go bez wywoływania init pierwszy) (naprawdę nie Singleton dłużej jednak)

private static Singleton instance; 
private SomeDataObject data; 

private Singleton(String path) { loadDataFromFile(path); ... } 

public static Singleton getInstance() { 
    return instance; 
} 

public static void init(String dataPath){ 
    instance = new Singleton(dataPath); 
} 

Potem jest: (możliwe praktyki złe kodowanie bok)

class Main 
{ 
    public static void main(String[] args) 
    { 
    Singleton.currentPath = "somepath"; 
    ... 
    } 
} 

class Singleton 
{ 
    public static String currentPath = null; 
    private static Singleton instance; 
    private SomeDataObject data; 

    private Singleton(String path) { loadDataFromFile(path); ... } 

    public static Singleton getInstance() { 
    if(instance == null && currentPath != null) 
     instance = new Singleton(currentPath); 
    return instance; 
    } 
} 

, co jak sądzę, tak naprawdę nie rozwiązuje zbyt wiele.

+0

Ale mogę także zrobić Singleton.getInstance(). DoStuff() i to byłby błąd, ponieważ brak inicjalizacji zostało zrobione. Jak napisałem w moim poście, w moim kodzie mogę to śledzić, ale ogólnie to niczego nie rozwiązuje. – jnovacho

+0

@jnovacho Możesz kazać konstruktorowi odczytać zmienną statyczną przechowywaną gdzieś indziej, ale przypuszczam, że jest to zasadniczo ten sam problem. Nie sądzę, że możesz zrobić lepiej niż to, co podałem w odpowiedzi (chyba że chcesz przekazać ścieżkę do każdego wywołania 'getInstance'). – Dukeling

+0

dla mnie pierwszy działał z public static void init() –

0

Używam czegoś, co jest "bardziej" bezpieczne dla wątków niż obecne rozwiązanie wygrywające, prawie bez synchronizacji.

import java.util.function.Supplier; 

public class InitOnce { 

/** 
* Marked as final to prevent JIT reordering 
*/ 
private final Supplier<String> theArgs; 

private InitOnce(Supplier<String> supplier) { 
    super(); 
    this.theArgs = supplier; 
} 

/** 
* Uses the arguments to do something 
* 
* @return 
*/ 
public String doSomething() { 
    return "Something : " + theArgs.get(); 
} 

/** 
* Initializes all the things 
* 
* @param someArgs 
*/ 
public static synchronized void init(final Supplier<String> someArgs) { 
    class InitOnceFactory implements Supplier<InitOnce> { 
     private final InitOnce initOnceInstance = new InitOnce(someArgs); 

     @Override 
     public InitOnce get() { 
      return initOnceInstance; 
     } 
    } 

    if (!InitOnceFactory.class.isInstance(instance)) { 
     instance = new InitOnceFactory(); 
    } else { 
     throw new IllegalStateException("Already Initialized"); 
    } 
} 

private static Supplier<InitOnce> instance = new InitOnceHolder(); 

/** 
* Temp Placeholder supplier 
* 
*/ 
private static final class InitOnceHolder implements Supplier<InitOnce> { 
    @Override 
    public synchronized InitOnce get() { 

     if (InitOnceHolder.class.isInstance(instance)) 
      throw new IllegalStateException("Not Initialized"); 

     return instance.get(); 
    } 

} 

/** 
* Returns the instance 
* 
* @return 
*/ 
public static final InitOnce getInstance() { 
    return instance.get(); 
} 
}