2

I'm developing a Java application that needs some configuration files, but I always forget the property names and I find it not comfortable creating a get method for every configuration property. However I find them useful for the return type they provide for me.

I wonder how configurations are managed in well-design Java-apps but I'd like to know if it is a good practice if I do it using enums like this. I imagine how complicated it would become if they where localization files. But this idea is still seducing me.

public enum Confs {

    HLENGTH("hlength", Integer.class),
    ;

    private String propertyName;
    private Class type;


    private Confs(String propertyName, Class type) {
        this.propertyName = propertyName;
        this.type = type;
    }

    public Object getVal(){
// This hash map is loaded in a singleton class which uses java Properties feature to read the configuration file
        return MyLoadedConfigurationsSingleTon.getMap().get(this.propertyName);
    }

}

Anyway, isn't there a standard solution to solve this problem?

1 Answer 1

3

Keys

It really depends on your requirements. There are tons of options here. For simple applications I generally use something that maps arbitrary configuration keys to values. Then, the names of the keys can be specified as string constants, e.g., somewhere:

public static final String KEY_COLOR = "color";
public static final String KEY_SIZE = "size";

This, of course, presents a problem of where to specify these constants. You could specify them in some globally shared location (e.g. in your configuration class, or in some special constant values class, or even as an enum like you proposed). This has the advantage of letting you see all key names in one place, thus easily avoiding conflicts. This has the distinct major disadvantage, however, of breaking modularity and forcing all classes to be dependent on this collection of keys -- adding/removing/moving objects now requires you to modify your global key collection.

Another thing you could do is define configuration key names as string constants in the classes/packages that actually use them. This does not break modularity. However, you now run the risk of conflicting key names with other unrelated classes. Adding new keys means you have to go through every object that uses your configuration and make sure the new key name is not already in use.

However, the conventional Java solution to that is to have the key names also include the package (and possibly class name) of the class that uses them. E.g.:

package com.me.whatever;
public class Something {
    static final String KEY_COLOR = "com.me.whatever.Something.color";
    static final String KEY_SIZE = "com.me.whatever.Something.size";
}

package com.me.util;
public class Other {
    static final String KEY_SIZE = "com.me.util.Other.size";
    static final String KEY_GROUP = "com.me.util.Other.group";
}

Sometimes it makes more sense to specify the package name only, if that is more representative of your situation.

The above ideas also hold true for other configuration schemes. E.g. with the Preferences API, the path to the keys can be derived from the package names. That API even provides things like systemNodeForPackage() that take care of this for you.

So, just do whatever leads to the clearest, most maintainable code. Find a balance between simplicity, modularity, and flexibility. For simple throw-away applications there's nothing wrong with breaking "OOP" concepts and sticking them all in one place, as long as what you are doing is clear. Otherwise, store the key values where they are primarily used, and take advantage of package and class names to ensure uniqueness and avoid key namespace pollution.

Note that an enum may not necessarily be the most appropriate or convenient data type to store key names in, if key names are split between multiple packages. You could certainly come up with some clever system to make that work, but more often than not string constants are both adequate and easy to work with. However, an enum could be an appropriate solution if you are storing more than just the key (e.g. the value type).


Value Types

As for value types by the way, you have many options there too. You could have conversion be done by the client class instead of enforcing it in the configuration side -- but, of course, the latter is very convenient in many cases.

The problem with using an enum is that you are essentially defining all your configuration keys in one place (see above) and it is difficult to extend the available sets of configuration keys in a modular way when classes are added or removed.

You could create a general purpose configuration key class, that can be instantiated as needed, instead of using fixed enum, e.g.:

public class Conf {

    public static class Key {

        final String key; 
        final Class<?> type;

        public Key (String key, Class<?> type) {
            this.key = key;
            this.type = type;
        }

    }

    public Object getValue (Key key) {
        ...
    }

}

The above can be easily improved through use of generic types.

Then in your client classes:

package com.me.whatever;
public class Something {
    static final Conf.Key KEY_COLOR = new Conf.Key("com.me.whatever.Something.color", Color.class);
    static final Conf.Key KEY_SIZE = new Conf.Key("com.me.whatever.Something.size", Integer.class);
}

Speaking of key names again: You could even make the package name prefix addition be a function of Conf.Key, similar to how Preferences.systemNodeForPackage() works, by making it take the declaring class's type as a parameter and extracting the package name, so the above declarations become, e.g.:

static final Conf.Key KEY_COLOR = new Conf.Key(Something.class, "color", Color.class);

Conclusion

I'm glossing over a lot because, like I said, there are infinite options. I can't really cover every case of every option here but hopefully you get the idea. It's more about approaching it in a sane way, rather than approaching it in one specific "correct" way.

Sign up to request clarification or add additional context in comments.

1 Comment

It is worth mentioning that having a configuration key class like the above has some nice potential side effects. For example, each class could register its list of Conf.Key objects (or the Conf.Key could register itself when instantiated) and then that list can be used to e.g. generate user documentation at runtime on the fly. Default values can be encapsulated in the key classes and that information used to generate a default configuration file. Validation rules can be included as well. Etc, etc.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.