2015-04-06 6 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(); 
} 

прибудете скомпилирован точно тот же байт-код. Поэтому использование ненужной статической переменной строго хуже.


Но пока мы не готовы масштабироваться до рамки DI-приводом,

Discover Guice. Не называйте это фреймворком (хотя это и есть). Он прост в использовании и позволит вам избавиться от static.

Или сделать это вручную. Перепишите свой класс, сбросив все статические модификаторы и передав их везде, где это необходимо. Иногда это довольно многословно, но заявляя зависимости явно, вы можете тестировать классы изолированно.

Так оно и есть, вы не можете проверить StratBand без попадания в базу данных, независимо от того, насколько тривиальным является тестируемый метод. Проблема заключается в соединении каждого экземпляра StratBand со списком всех StratBand.

Кроме того, вы не можете проверить поведение, зависящее от содержимого stratBands, поскольку оно всегда загружается из БД (конечно, вы можете заполнить свою БД соответственно, но это большая боль).

Для начала, я бы создал StratBandManager (или StratBands или любое другое имя, которое вам нравится) и переместить все статические функции на него. Для того, чтобы легко переход, я бы создать класс временного со статическими хелперами как

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

Тогда принизить все это и заменить его на DI (с помощью Guice или делать это вручную).


I find Guice полезный даже для небольших проектов. Накладные расходы крошечные, так как часто нет или практически нет конфигурации.

+0

Да, я много часов изучал Guice, хотя я еще не применял его и хотел бы. Учитывая нашу текущую бизнес-среду и нашу функцию, мы взяли минималистский подход ... и по минималистскому я имею в виду консолидацию и сокращение числа классов, если это упростило ситуацию. Мне определенно нужно будет рассмотреть ваше предложение о переходе, поскольку сообщество кажется довольно непреклонным в отношении развязки и использования DI. – tmn

+0

@ThomasN. Я бы не уменьшил количество классов. Возможно, вы захотите использовать вложенные классы для «менеджеров», чтобы поддерживать минимальное количество файлов, но дополнительные классы по существу бесплатны (за исключением стоимости памяти, которая может иметь значение на Android). – maaartinus

+0

Да, я увлекался вложенными классами, потому что он хранит все, что содержится и в одном месте. Я действительно понимаю, что это может вызвать переносимость деформации, когда передается определенный порог сложности. Я думаю, что немного собираюсь немного изменить свой подход и начать использовать парадигмы, связанные с пакетом, а также подходить к проектам, дружественным к DI. – tmn