Під час роботи програма спирається на обчисленнях чи вводе-выводе використовується як змінні, а й явно прописані константи. Допустимо Ви обробляєте фінансові дані і по ходу коду кілька разів виводьте результати (нехай у консоль) приблизно таким чином:
System.out.println("Разомвое количество =" + countLoans);
...
String strCount = "Разомвое количество :" + loans.calc();
...
System.out.println(strCount + strSumDesc);
І це дуже погано з кількох причин.
-
Порушення принципу DRY (Don't repeat yourself - не повторюй).
Дублювання тексту та, як наслідок, збільшення розмірів скомпільованого коду. Також при цьому можливі описки. -
Порушення принципу KISS (keep it simple stupid – робіть речі простіше).
У разі потреби зміни такого роду констант доведеться прошерстити весь код. -
Кожна зміна константи вимагатиме перекомпіляції коду.
-
Введення мультимовності вимагатиме (за такої побудови коду) багато додаткових конструкцій до кожної такої константи.
static final
та їх ініціалізацією + застосування по ходу коду. Тобто. створюємо:
public class Constants {
public static final int COUNT_DEPARTMENTS = 20;
public static final String MSG_TOTAL_AMOUNT = "Разомвое количество";
...
}
і застосовуємо приблизно як System.out.println(Constants.MSG_TOTAL_AMOUNTCOUNT_DEPARTMENT + "= " + countLoans);
Якщо нам необхідно змінити фразу, ми робимо тільки в одному місці . Щоб усунути третю причину можна скористатися класом Properties
, більш докладно на відповідних лекціях JavaRush і деяких big-завданнях другої половини 20-х рівнів. Найчастіше вживані змінні можна винести як окремі public static final
, що рідше використовуються. Отримувати через виклик методу PROPS.getProperty()
.
public class Constants {
public static final int COUNT_DEPARTMENTS;
public static final String MSG_TOTAL_AMOUNT;
public static final Properties PROPS;
static {
Logger log = LogManager.getLogger(Constants.class);
PROPS = new Properties();
try{
fis = new FileInputStream("confs/config.properties");
PROPS.load(fis);
fis.close();
} catch ( IOException e) {
log.error(e.getMessage());
}
MSG_TOTAL_AMOUNT = PROPS.getProperty("msg.total.amount");
COUNT_DEPARTMENTS = Integer.parseInt(PROPS.getProperty("count.departments"));
...
}
}
Залишилося розібратися з інтернаціоналізацією. Зробимо так, щоб у static
блоці відбувалося завантаження потрібного файлу properties
. Для цього працюватимемо з ResourceBundle
. Зазначимо, що із ResourceBundle
стандартними методами можна зчитувати значення ключів, але не встановлювати їх. Тому, як і раніше, зберігаємо в окремому Properties (config.properites)
файлі параметри, що не мають відношення до інтернаціоналізації (до того ж деякі з них можна змінювати в процесі роботи програми). Наприклад, мова інтерфейсу. Зручний інтерфейс IDEA допоможе нам. Створюємо його: Додаємо потрібні мови: У структурі проекту IDEA відображається як: Наповнюємо вмістом:
public class Constants {
public static final int COUNT_DEPARTMENTS;
public static final String MSG_TOTAL_AMOUNT;
public static final Properties PROPS;
public static final ResourceBundle UI_LANGUAGE;
static {
Logger log = LogManager.getLogger(Constants.class);
PROPS = new Properties();
try{
fis = new FileInputStream("confs/config.properties");
PROPS.load(fis);
fis.close();
} catch ( IOException e) {
log.error(e.getMessage());
}
if (PROPS.getProperty("ui.language").equalsIgnoreCase("RUS"))
UI_LANGUAGE = ResourceBundle.getBundle("messages", CharsetControl.RUS);
else
UI_LANGUAGE = ResourceBundle.getBundle("messages", CharsetControl.ENG);
MSG_TOTAL_AMOUNT = UI_LANGUAGE.getString("msg.total.amount");
COUNT_DEPARTMENTS = Integer.parseInt(PROPS.getProperty("count.departments"));
...
}
}
Ми домоглися, що мова інтерфейсу змінюється без втручання у код . До того ж ми отримали чіткий поділ праці : можемо віддати російський properties
файл перекладачеві-гуманітарію, який у зручному для себе текстовому редакторі його переведе. Нам залишиться лише перейменувати його в потрібне ім'я файлу та розмістити назад у ResourceBundle
. Невелике доповнення (використання CharsetControl
): Properties файли не чутливі до кодування (обробляють вміст файлу виключно по одному байту, що дорівнює використання ISO8859-1) і кирабоця в Properties
-файлу буде відображена некоректно. Щоб уникнути цього, можна:
-
Переганяти кирабочні символи в ESCAPE-послідовності \uxxxx через native2ascii
-
Допомогти
Properties
передавши йому необхіднийResourceBundle.Control
, у якому нам необхідно перевантажити метод (код знайшов на просторах інтернету + трохи модифікував).
import java.io.*;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.Charset;
import java.security.*;
import java.util.*;
public class CharsetControl extends ResourceBundle.Control {
public static final CharsetControl UTF_8 = new CharsetControl("utf8");
public static final CharsetControl RUS = new CharsetControl("cp1251");
public static final Locale ENG = new Locale("en","GB");
private Charset charset;
public CharsetControl(String charset) {
this(Charset.forName(charset));
}
public CharsetControl(Charset charset) {
this.charset = charset;
}
public Charset getCharset() {
return charset;
}
@Override
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
throws IllegalAccessException, InstantiationException, IOException {
String bundleName = toBundleName(baseName, locale);
ResourceBundle bundle = null;
if (format.equals("java.class")) {
try {
Class<? extends ResourceBundle> bundleClass =
(Class<? extends ResourceBundle>)loader.loadClass(bundleName);
if (ResourceBundle.class.isAssignableFrom(bundleClass)) {
bundle = bundleClass.newInstance();
} else {
throw new ClassCastException(bundleClass.getName()
+ " cannot be cast to ResourceBundle");
}
} catch (ClassNotFoundException e) {
}
} else if (format.equals("java.properties")) {
final String resourceName = toResourceName(bundleName, "properties");
final ClassLoader classLoader = loader;
final boolean reloadFlag = reload;
InputStream stream = null;
try {
stream = AccessController.doPrivileged(
new PrivilegedExceptionAction<InputStream>() {
public InputStream run() throws IOException {
InputStream is = null;
if (reloadFlag) {
URL url = classLoader.getResource(resourceName);
if (url != null) {
URLConnection connection = url.openConnection();
if (connection != null) {
connection.setUseCaches(false);
is = connection.getInputStream();
}
}
} else {
is = classLoader.getResourceAsStream(resourceName);
}
return is;
}
});
} catch (PrivilegedActionException e) {
throw (IOException) e.getException();
}
if (stream != null) {
try {
bundle = new PropertyResourceBundle(new InputStreamReader(stream, getCharset()));
} finally {
stream.close();
}
}
} else {
throw new IllegalArgumentException("unknown format: " + format);
}
return bundle;
}
}
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ