2015-04-06 16 views
6

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 


    } 
+2

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. –

+0

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

+1

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. –

Odpowiedz

3

Więc zastanawiam się czy to jest kosher używać statycznego initializer

Najśmieszniejsze jest to, że

private static final boolean subscribed = subscribe(); 

i

private static final boolean subscribed; 
static { 
    subscribed = subscribe(); 
} 

get skompilowany do dokładnie tego ten sam kod bajtowy. Używanie niepotrzebnej zmiennej statycznej jest więc znacznie gorsze.


Ale dopóki jesteśmy gotowi, aby przeskalować do ramy DI-driven,

Discover Guice. Nie nazywaj tego frameworkiem (choć tak jest). Jest łatwy w użyciu i pozbyć się static.

Lub zrób to ręcznie. Przepisz swoją klasę, upuszczając wszystkie modyfikatory statyczne i przekazuj ją w dowolnym miejscu. Czasami jest to dość gadatliwe, ale stwierdzenie zależności wyraźnie pozwala przetestować klasy w izolacji.

Tak jak to jest możliwe, nie można przetestować StratBand bez uderzania w bazę danych, niezależnie od tego, jak trywialna jest testowana metoda. Problem polega na połączeniu każdej instancji StratBand z listą wszystkich StratBand s.

Ponadto, nie można przetestować zachowania zależnego od zawartości stratBands, ponieważ zawsze jest ona ładowana z DB (oczywiście, możesz wypełnić swoje DB odpowiednio, ale to jest wielki ból).

Na początek utworzę StratBandManager (lub StratBands lub dowolną nazwę, którą lubisz) i przeniesiesz do niej wszystkie funkcje statyczne. W celu łatwego przejścia, chciałbym stworzyć tymczasowy klasę z pomocników statycznych jak

private static StratBandManager stratBandManager = new StratBandManager(); 
public static ImmutableList<StratBand> stratBands() { 
    return stratBandManager.stratBands(); 
} 

Następnie potępiać wszystko i zastąpić je przy użyciu Guice di (lub robi to ręcznie).


I find Guice przydatne nawet dla małych projektów. Koszt jest niewielki, ponieważ często nie ma żadnej konfiguracji.

+0

Tak, spędziłem wiele godzin na studiowaniu Guice, chociaż jeszcze go nie zastosowałem i chciałbym. Biorąc pod uwagę nasze obecne otoczenie biznesowe i naszą funkcję, przyjęliśmy podejście minimalistyczne ... a przez minimalistę rozumiem konsolidację i redukcję liczby klas, jeśli to uprości. Zdecydowanie będę musiał rozważyć Twoją propozycję przejścia, ponieważ społeczność wydaje się być dość nieugięta w kwestii oddzielenia i użycia DI. – tmn

+0

@ThomasN. Nie zminimalizowałbym liczby klas. Możesz użyć klas zagnieżdżonych dla "menedżerów", aby utrzymać niską liczbę plików, ale dodatkowe klasy są zasadniczo bezpłatne (z wyjątkiem kosztów pamięci, które mogą mieć znaczenie dla Androida). – maaartinus

+0

Tak, lubiłem zagnieżdżone klasy, ponieważ wszystko trzyma się w jednym miejscu. Zdaję sobie sprawę, że może to spowodować utrzymanie zdolności do obciążania, gdy zostanie przekroczony pewien poziom złożoności. Myślę, że zamierzam nieco zmienić moje podejście i zacząć bardziej wykorzystywać paradygmaty pakiet-prywatny, a także podejść do projektów przyjaznych dla DI. – tmn

Powiązane problemy