2015-04-06 106 views
6

我記得幾年前,我正在使用靜態初始化器來調用類級別的設置操作。我記得它有非常奇怪的行爲,我只是決定避開它們。也許是因爲我搞亂了最高層的命令或是一個新手。但我遇到需要重新審視它們,我想確保沒有更簡潔的更好的方法。靜態初始化的合法用法?

我知道這不是時尚,但我經常有數據驅動的類,它們維護從數據庫導入的實例的靜態列表。

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 
} 

當我有幾十個表驅動類像這樣的,這種模式是非常簡潔(是的,我知道它緊密結合數據/實例中的一個源類)。

但是,當我發現Google Guava的優點時,我希望使用EventBus在發佈某個事件時更新靜態列表。我會創建一個靜態最終布爾變量來調用一個初始化註冊的靜態方法。

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

這讓人討厭得很快,因爲編譯器會針對永遠不會使用的訂閱變量發出警告。此外,它只是增加了混亂。所以我想知道如果使用靜態初始化器是否可行,並且如果我不將它解耦成兩個或更多類,那麼確實沒有更好的方法。思考?

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

這是「不時髦」,正是因爲,正如你發現的那樣,它確實導致了「非常離奇的行爲」。通常最好不要讓這些東西是靜態的,而是將它們傳遞到明確需要的地方(或者隱式地使用依賴注入)。當他們只是創建單例或者進行純計算時,靜態初始化器是很好的,但是它們通常不應該與文件系統,數據庫或任何外部的任何事情交互。 –

+0

我在等人說這個。我得到了我欣賞的DI範例。但是,在我們準備擴大到DI驅動的框架之前,我有點希望保持我們有更長一段時間。 – tmn

+1

你不需要在這裏使用DI,但是如果你能夠完全實現這個功能,我會感到很驚訝,而且它會保持非常脆弱,難以測試,並且坦率地說,要花費更多的努力將採取。 –

回答

3

所以我想知道如果它是猶太使用靜態初始化

有趣的是,

private static final boolean subscribed = subscribe(); 

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

GET編譯到完全一樣相同的字節碼。所以使用不必要的靜態變量是嚴格的更糟。


但是,直到我們準備擴展到一個DI驅動的框架,

發現Guice。不要稱之爲框架(儘管如此)。它很容易使用,讓我們擺脫static

或者手動完成。通過刪除所有靜態修飾符並將其傳遞到需要的地方來重寫您的類。它有時比較冗長,但明確聲明依賴性可以讓你獨立地測試類。

它是這樣的,無論測試方法多麼微不足道,您都無法測試StratBand而無法擊中數據庫。問題是每個StratBand實例與所有StratBand的列表的耦合。因爲它總是從數據庫中加載(當然,你可以相應地填充你的數據庫,但這是一個很大的麻煩),你不能測試依賴於stratBands內容的行爲。對於初學者,我會創建StratBandManager(或StratBands或任何你喜歡的名稱),並將所有靜態功能移動到它。爲了便於過渡,我想創建一個臨時靜態傭工像

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

然後棄用這一切,並通過DI(使用吉斯或做手工)替換它。


I find Guice即使對於小項目也很有用。經常沒有或幾乎沒有任何配置,開銷很小。

+0

是的,我花了很多時間學習Guice,雖然我還沒有申請它,並希望。考慮到我們目前的商業環境和我們的功能,我們採取了極簡主義的方法......而通過極簡主義,我的意思是鞏固和減少類的數量,如果它簡化了事情。我肯定會需要考慮你的轉換建議,因爲社區似乎堅持解耦和使用DI。 – tmn

+0

@ThomasN。我不會盡量減少班級數量。您可能希望爲「管理員」使用嵌套類,以便保持較低的文件數量,但其他類本質上是免費的(除了Android上可能存在的內存成本)。 – maaartinus

+0

是的,我一直喜歡嵌套類,因爲它將所有內容保存在一個地方。我認識到當一定的複雜性閾值通過時它可能會導致應變可維護性。我想我會進一步發展我的方法,並開始更多地利用封裝私有範例,以及接近DI友好型設計。 – tmn