Effective coding of parameter control with variable types in Java

I am currently implementing a simulation in Java that requires input of about 30 different parameters. In the end, I want to be able to read these parameters from a file, as well as from the graphical interface, but now I'm just focused on entering files. My modeling requires parameters that have different types: strings, ints and double, and currently I have them as fields for modeling, for example.

private String simName; private int initialPopulationSize; private double replacementRate; 

Since these parameters are not all the same, I cannot store them in an array, and I have to read each separately using the same code about 30 times. An example for three parameters:

 //scanner set up and reading each line, looking for "(key)=(param)" regex matches //if statement to check each param name against the key matched in file. Store param in that field if the name matches. String key = m.group(1); if (key.equals(PKEY_SIM_NAME)) { if (simNameSet) { throw new IllegalStateException("multiple values for simulation name"); } this.simName = m.group(2); simNameSet = true; } else if (key.equals(PKEY_INITIAL_SIZE)) { if (initialSizeSet) { throw new IllegalStateException("multiple values for initial population size"); } this.initialPopulationSize = Integer.parseInt(m.group(2)); initialPopulationSize = true; } else if (key.equals(PKEY_MUT_REPLACEMENT)) { if (replacementRateSet) { throw new IllegalStateException("multiple values for replacement rate"); } this.replacementRate = Double.parseDouble(m.group(2)); replacementRateSet = true; } //Add nauseum for each parameter..... 

So, I currently have a long and obscure method for reading in parameters, and I probably have to do the same thing again for reading from gui.

The best alternative that I thought of is to read everything in string fields first. That way, I can write some simple lines to read using Map. Something like this (untested code):

 //This time with a paramMap<String, String>, scanner set up as before if (!paramMap.containsKey(key)) { paramMap.put(key, m.group(2)); } else{ throw new IllegalStateException("multiple values for initial population size"); } 

However, this will be inconvenient when it comes to using these parameters from the map, since I will have to discard parameters other than String whenever and wherever I want to use them.

At this moment, I feel that this is my best approach. I want to know if someone more experienced can develop a better strategy to deal with this situation before I start.

+4
source share
4 answers

You can define a base class or parameter interface, for example:

 interface Parameter { void parse(String s); Object getValue(); ... } 

and a class for each type of parameter that you want to have, for example. IntParameter , DoubleParameter , StringParameter . Here's a sketch of an IntParameter :

 class IntParameter implements Parameter { private int value; public void parse(String s) { value = Integer.parseInt(s); } public Object getValue() { return value; } ... } 

You can then save your parameters in Map<String, Parameter> and populate them from various sources, such as command line parameters or properties.


A safer but more complex decision can be made if you do not store the value in parameter objects, but create parameters of static objects that are used to access their values. This is shown in the following example:

 abstract class Parameter { private String name; public Parameter(String name) { this.name = name; } public abstract Object parse(String s); } class IntParameter extends Parameter { public static final IntParameter ANSWER = new IntParameter("answer"); // Add more options here. public IntParameter(String name) { super(name); } public Object parse(String s) { return Integer.parseInt(s); } } class Parameters { private Map<Parameter, Object> params = new HashMap<Parameter, Object>(); public int get(IntParameter p) { return (Integer)params.get(p); } public void put(IntParameter p, int value) { params.put(p, value); } public void putString(Parameter p, String value) { params.put(p, p.parse(value)); } } 

This allows you to access the parameter in a safe manner:

 Parameters params = new Parameters(); params.putString(IntParameter.ANSWER, "42"); // parse and store the value int value = p.get(IntParameter.ANSWER); 

The solution can be extended to other types.

+1
source

Parsing such files is a bad idea. This is already done for you!

The properties

The mechanism you can use is called "properties" in Java. This is a plain text file containing key-value pairs, as you described. This is usually an extension of *.properties .

You do not need to use Spring or any other structure, as it is part of the regular JDK. So feel free to use it for your project!

Want to be typical? Use the settings!

if you want to be typical, use "preferences" instead. The Preference API is an extension of the regular Property API that guarantees you the type of property you need.

Documentation

Using all of this is very simple. I do not want to repeat the documentation, so please check these two resources: Property API ,

+2
source

You can use the properties file for your situation. The properties file consists of a key / wap pair. A good utility for this statement is to use spring. Sample:

  <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>[your-path-1]</value/> <value>[your-path-2]</value/> </list> </property> </bean> 

and for key / value to read:

  <bean id="pum" class="org.springframework.orm.jpa.persistenceunit.DefaultPersistenceUnitManager"> <property name="persistenceXmlLocations" value="${your-key-1}"/> </bean> 

Spring set the value of the your-key-1 element of the persistenceXmlLocations keyword. A good advantage of using spring is Type Casting , spring's own type of cast property for a variable type.

0
source

The problem you are talking about is one of the main reasons why I started and supported a tool called InPUT . The Preferences API will work for your simple example, but by default it does not offer content checking, value ranges, or handling of non-primitive types (except strings). If you are dealing with simulations or optimizations, you can read on.

You can define your parameters in the XML design space descriptor as follows:

 <SParam id="simName" type="String"/> <NParam id="initialPopulationSize" type="integer" inclMin="10" inclMax="100"/> <NParam id="replacementRate" type="double" inclMin="0.3" inclMax="0.7"/> 

NParam (numeric) denotes primitive parameters, SParam (structural) for type parameters when it comes to Java.

Your configuration (say "config.xml") can now contain the following values:

 <SValue id="simName" value="someName"/> <NValue id="initialPopulationSize" value="20"/> <NValue id="replacementRate" value="0.42"/> 

From the code, all you do is call:

 Design input = new Design("config.xml"); String simName = input.get("simName"); int initialPopulationSize = input.get("initialPopulationSize"); double replacementRate = input.get("replacementRate"); 

InPUT ensures that mappings match your descriptors. After you configure this, you can add / remove, change alternatives of your own free will, without touching the code. You can use it to enter complex nested classes (see the InPUT main page, Example ) in the code conversion descriptor or define the relationship between numerical values ​​in the parameter descriptor (for example, the elite size is always smaller than the population size). You can also create projects arbitrarily, depending on the design space (for example, to search for optimal parameters). The goal is to make the tool available for various programming languages, for a simpler language agnostic exchange of experimental descriptors (C ++ is in the process of being created). You can find many Java examples here .

0
source

Source: https://habr.com/ru/post/1396385/


All Articles