Pamiętam, że kilka lat temu używałem inicjalizatorów statycznych do wywoływania operacji instalacyjnych na poziomie klasy. Pamiętam, że miałam bardzo dziwne zachowania i po prostu postanowiłam się od nich uwolnić. Może dlatego, że bałam się najwyższej kolejności lub bycia nowicjuszem. Ale spotykam się z koniecznością ich ponownej wizyty i chcę się upewnić, że nie ma lepszego sposobu, który byłby równie zwięzły.Prawidłowe zastosowania dla inicjalizatora statycznego?
Wiem, że nie jest modne, ale często mam klasy oparte na danych, które utrzymują statyczną listę instancji importowanych z bazy danych.
public class StratBand {
private static volatile ImmutableList<StratBand> stratBands = importFromDb();
private final int minRange;
private final int maxRange;
private static ImmutableList<StratBand> importFromDb() {
//construct list from database here
}
//constructors, methods, etc
}
Kiedy mam dziesiątki klas stole napędzane jak ten, ten wzór jest bardzo zwięzły (tak wiem, że to mocno pary klasy z jednego źródła danych/instancji).
Jednak, gdy odkryłem dobroć Google Guava, chcę użyć EventBus do aktualizacji listy statycznej, gdy jakieś wydarzenie zostanie opublikowane. Stworzyłbym statyczną ostateczną zmienną boolowską tylko po to, aby wywołać statyczną metodę, która zainicjowała rejestrację.
public class StratBand {
private static volatile ImmutableList<StratBand> stratBands = importFromDb();
private static final boolean subscribed = subscribe();
private final int minRange;
private final int maxRange;
private static ImmutableList<StratBand> importFromDb() {
//construct list from database here
}
//constructors, methods, etc
private static boolean subscribe() {
MyEventBus.get().register(new Object() {
@Subscribe
public void refresh(ParameterRefreshEvent e) {
stratBands = importFromDb();
}
});
return true;
}
}
Bardzo szybko stało się to irytujące, ponieważ kompilator nigdy nie używał ostrzeżeń przed zasubskrybowaną zmienną. Ponadto, po prostu dodaje bałaganu. Zastanawiam się więc, czy korzystanie ze statycznego inicjalizatora jest koszerne i naprawdę nie ma lepszego sposobu, jeśli nie podzielę tego na dwie lub więcej klas. Myśli?
public class StratBand {
private static volatile ImmutableList<StratBand> stratBands = importFromDb();
static {
MyEventBus.get().register(new Object() {
@Subscribe
public void refresh(ParameterRefreshEvent e) {
stratBands = importFromDb();
}
});
}
private final int minRange;
private final int maxRange;
private static ImmutableList<StratBand> importFromDb() {
//construct list from database here
}
//constructors, methods, etc
}
To "nie jest modne" właśnie dlatego, ponieważ, jak odkryłeś, powoduje "bardzo dziwne zachowania". Ogólnie rzecz biorąc, lepszą praktyką jest, aby te rzeczy w ogóle nie były statyczne, aby przekazywać je tam, gdzie są potrzebne bezpośrednio (lub pośrednio przy zastrzyku zależności). Inicjatory statyczne są dobre, gdy tylko tworzą pojedynki lub robią czyste obliczenia, ale generalnie nie powinny wchodzić w interakcję z systemem plików, bazami danych lub czymkolwiek na zewnątrz. –
Czekałem, aż ktoś to powie. Otrzymuję paradygmat DI, który doceniam. Ale dopóki nie będziemy gotowi na skalowanie do schematu opartego na DI, mam nadzieję, że uda nam się utrzymać to, co mamy nieco dłużej. – tmn
Nie musisz używać DI tutaj, ale byłbym zaskoczony, gdybyś mógł w ogóle to zrobić, i pozostanie bardzo delikatny, trudny do testowania, i szczerze mówiąc, będzie to wymagało więcej wysiłku niż robienie tego właściwie wziąłbym. –