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;
}
}
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ