System.out.println("Totalвое количество =" + countLoans);
...
String strCount = "Totalвое количество :" + loans.calc();
...
System.out.println(strCount + strSumDesc);
And this is very bad for several reasons.
-
Violation of the DRY principle (Don't repeat yourself - do not repeat).
Duplication of text and, as a result, an increase in the size of the compiled code. Also, there may be typos. -
Violation of the KISS principle (keep it simple stupid - make things easier).
If you need to change such constants, you will have to go through the entire code. -
Each constant change will require recompilation of the code.
-
The introduction of multilingualism will require (with such a construction of the code) a lot of additional constructions for each such constant.
static final
and their initialization + applying them in the course of the code. Those. create:
public class Constants {
public static final int COUNT_DEPARTMENTS = 20;
public static final String MSG_TOTAL_AMOUNT = "Totalвое количество";
...
}
and apply something like System.out.println(Constants.MSG_TOTAL_AMOUNTCOUNT_DEPARTMENT + "= " + countLoans);
If we need to change the phrase, we do only in one place . To eliminate the third reason, you can use the class Properties
, see the corresponding CodeGym lectures and some big-tasks of the second half of the 20th levels for more details. The most frequently used variables can be taken out as separate public static final
, less frequently used ones. Receive via method call 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"));
...
}
}
It remains to deal with internationalization. Let's make sure that static
the necessary file is loaded in the block properties
. To do this, we will work with ResourceBundle
. Note that ResourceBundle
you can read key values from standard methods, but not set them. Therefore, we still store in a separate Properties (config.properites)
file the parameters that are not related to internationalization (besides, some of them can be changed during the course of the program). For example, interface language. The convenient IDEA interface will help us. Create it: Add the necessary languages: In the project structure for IDEA, it is displayed as: Fill it with content:
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"));
...
}
}
We have achieved that the interface language changes without interfering with the code . In addition, we got a clear division of labor : we can give the Russian properties
file to a humanities translator, who will translate it in a text editor that is convenient for him. We just have to rename it to the desired filename and place it back in ResourceBundle
. A small addition (using CharsetControl
): Properties files are not sensitive to encoding (they process the contents of the file only one byte at a time, which is equivalent to using ISO8859-1) and Cyrillic in the Properties
-file will be displayed incorrectly. To avoid this, you can:
-
Convert Cyrillic characters to ESCAPE sequence \uxxxx via native2ascii
-
Help
Properties
by passing it the necessary oneResourceBundle.Control
, in which we need to overload the method (the code was found on the Internet + slightly modified).
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;
}
}
GO TO FULL VERSION