Где инициализировать объект свойств Java? - PullRequest
3 голосов
/ 18 декабря 2008

Я унаследовал приложение, которое использует файл свойств Java для определения параметров конфигурации, таких как имя базы данных.

Существует класс MyAppProps, который выглядит следующим образом:

public class MyAppProps {

   protected static final String PROP_FILENAME = "myapp.properties";
   protected static Properties myAppProps = null;

   public static final String DATABASE_NAME = "database_name";
   public static final String DATABASE_USER = "database_user";
   // etc...

   protected static void init() throws MyAppException {
     try {
       Classloader loader = MyAppException.class.getClassLoader();
       InputStream is = loader.getResourceAsStream(PROP_FILENAME);
       myAppProps = new Properties();
       myAppProps.load(is);
     } catch (Exception e) {
       threw new MyAppException(e.getMessage());
     }
    }

    protected static String getProperty(String name) throws MyAppException {
      if (props==null) {
        throw new MyAppException("Properties was not initialized properly.");
      }
      return props.getProperty(name);
    }
  }

Другие классы, которым необходимо получить значения свойств, содержат код, такой как:

String dbname = MyAppProps.getProperty(MyAppProps.DATABASE_NAME);

Конечно, перед первым вызовом MyAppProps.getProperty необходимо инициализировать MyAppProps следующим образом:

MyAppProps.init();

Мне не нравится тот факт, что init() нужно вызывать. Разве инициализация не должна происходить в статическом блоке инициализации или в частном конструкторе?

Кроме того, что-то еще кажется неправильным в коде, и я не могу понять, как это сделать. Экземпляры свойств обычно заключены в настраиваемый класс? Есть ли здесь что-то еще, что не так?

Ответы [ 4 ]

5 голосов
/ 18 декабря 2008

Если я создам свой собственный класс-обертку, как это; Я всегда предпочитаю делать строго типизированные геттеры для значений, вместо того, чтобы выставлять все внутренние операции через статические конечные переменные.

private static final String DATABASE_NAME = "database_name"
private static final String DATABASE_USER = "database_user"
public String getDatabaseName(){
   return getProperty(MyAppProps.DATABASE_NAME);
}
public String getDatabaseUser(){
   return getProperty(MyAppProps.DATABASE_USER);
}

Статический инициализатор выглядит так:

static {
   init();
}

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

Вы можете рассмотреть возможность просмотра структур внедрения зависимостей (DI), таких как spring или guice, они позволят вам ввести соответствующее значение непосредственно в места, где вам нужно их использовать, вместо того, чтобы проходить через косвенные ссылки дополнительного класса. Многие люди считают, что использование этих платформ уменьшает внимание к этому виду программного кода - но только после того, как вы закончите изучение структуры фреймворка. (DI-фреймворки быстро изучаются, но освоить их нужно довольно много времени, так что это может быть большим молотком, чем вы действительно хотите)

3 голосов
/ 18 декабря 2008

Причины использования статического инициализатора:

  • Не могу забыть назвать это

Причины использования функции init ():

  • Вы можете передать ему параметры
  • Легче обрабатывать ошибки

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

myapp.data.path=${myapp.home}/data

Кроме того, в вашем инициализаторе вы можете воспользоваться перегрузкой файла свойств:

  • Загрузка в "myapp.properties" из пути к классам
  • Загрузка в "myapp.user.properties" из текущего каталога с помощью конструктора переопределения свойств
  • Наконец, загрузите System.getProperties () в качестве окончательного переопределения

Файл свойств "user" не входит в управление версиями, что приятно. Это позволяет избежать проблем, связанных с настройкой файла свойств и случайной проверкой его с помощью жестко заданных путей и т. Д.

Хорошие времена.

1 голос
/ 14 мая 2009

Проблема со статическими методами и классами заключается в том, что вы не можете переопределить их для двойников теста. Это делает модульное тестирование намного сложнее. У меня все переменные объявлены как финальные и инициализированы в конструкторе. Все, что необходимо, передается в качестве параметров конструктору (внедрение зависимости). Таким образом, вы можете заменить удвоенные значения тестов для некоторых параметров во время модульных тестов.

Например:

public class MyAppProps {

   protected static final String PROP_FILENAME = "myapp.properties";
   protected Properties props = null;

   public String DATABASE_NAME = "database_name";
   public String DATABASE_USER = "database_user";
   // etc...

   public MyAppProps(InputStream is) throws MyAppException {
     try {
       props = new Properties();
       props.load(is);
     } catch (Exception e) {
       threw new MyAppException(e.getMessage());
     }
    }

    public String getProperty(String name) {
      return props.getProperty(name);
    }
    // Need this function static so
    // client objects can load the
    // file before an instance of this class is created.
    public static String getFileName() {
      return PROP_FILENAME;
    }
}

Теперь вызовите его из производственного кода следующим образом:

String fileName = MyAppProps.getFileName();
Classloader loader = MyAppException.class.getClassLoader();
InputStream is = loader.getResourceAsStream(fileName);
MyAppProps p = new MyAppProps(is);

Внедрение зависимостей происходит, когда вы включаете входной поток в параметры конструктора. Хотя это немного сложнее, чем использование статического класса / Singleton, при выполнении модульных тестов все становится просто невозможным.

Для модульного тестирования это может выглядеть примерно так:

@Test
public void testStuff() {
    // Setup
    InputStringTestDouble isTD = new InputStreamTestDouble();
    MyAppProps instance = new MyAppProps(isTD);

    // Exercise
    int actualNum = instance.getProperty("foo");

    // Verify
    int expectedNum = 42;
    assertEquals("MyAppProps didn't get the right number!", expectedNum, actualNum);
}

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

1 голос
/ 18 декабря 2008

Вы можете использовать либо статический блок, либо конструктор. Единственный совет, который я имею, это использовать ResourceBundle. Это может лучше удовлетворить ваши требования. Для получения дополнительной информации перейдите по ссылке ниже.

Edit: ResourceBundles vs Properties

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...