0

Suppose that you have a Java project with several classes, most of them dealing with some constants (properties of your dataset, which are supposed to be loaded (based on your dataset) at the beginning of your experiment).

To become able to access these constants in the entire classes, a very reasonable option is putting the constants as fields of a container (object/class/Interface). Then for letting your other classes take advantage of the container field, you have 5 ways

Only if the field is dynamic:

  • passing the container to the dealing method (the method which deals with required constants)
  • putting the container in a field of the method-class (the class which your method is located in).

Nevertheless of the field being dynamic or static (when the container is a class)

  • extending the container of that field by the method-class.

Only if the field is static

  • implementing the container of that field by the method class.
  • calling the static fields (Container.field)

The first two options and the last one force you to write in your code tens or hundreds of time the container name + '.' (e.g. container.trainingListSize, container.numberOfUsers, Container.trainingListSize, Container.numberOfUsers, ...) which I am trying to get rid of that; the third option forces you to be the son of a special class which sometimes contradicts the structure of your program. It remains only the last option; but unfortunately in Java interfaces the constant values of interfaces are final and cannot be set, when you load your properties file, at the beginning of the program.

Looking at the question (Variables in Interface), it seems that the questioner was in a condition like me; but yet no solution I found.

Community
  • 1
  • 1
hossayni
  • 465
  • 1
  • 3
  • 16
  • Do Option 1 and accept the cost of the container name. – Louis Wasserman Apr 06 '15 at 15:32
  • Your question is a little bit confusing. Notice you meant to "change the constants of a Java interface". That's not possible per-se. Moreover, whatever is meant to be a constant it must not be changed, otherwise what's the point of having a "constant". I think you might want to elaborate on that and change your point a little bit, although by the nature of some of your explanation(s) I may infer you want to read some constant values from some place and used them. – x80486 Apr 06 '15 at 15:33
  • @Machina Maybe the word constant makes mis-understanding they are some fields in java and not constants but some constants for each dataset (constant properties of your dataset) – hossayni Apr 06 '15 at 15:35
  • @Machina I know that I cannot change the fields of Interface but I think that it is reasonable that Java provides a solution for avoiding to repeat the container name, in all over the project, several times. – hossayni Apr 06 '15 at 15:36
  • OK, got it...then use your very first approach, like @Louis Wasserman already told. That's by far the less cumbersome solution; although you can use (sometimes) a combination of some of the other, but use it wisely and cleanly (if that's possible)...remember that, in the end, we write code to be read/understood by others. – x80486 Apr 06 '15 at 15:41
  • One other thing to save the "Container." part are **static imports**. On the other hand, making things "static" can have ugly consequences for unit testing. But using it for "normal" "flat" constants (like strings, numbers) is mostly fine. – GhostCat Apr 06 '15 at 15:42

2 Answers2

1

I think you need to have clear intents and separation of concerns in your software architecture. You talk about "constants" but then you mention loading them from a file, so these are not really constants after all. These are more like configuration parameters. If that is correct, you have several options.

You can consider your configuration as a global variable: in that case I recommend creating a singleton class that loads the variables from the file and provides utility methods to access them, e.g., MyConfig.someParameter(). Notice that you can static import MyConfig.* and thus access the variables simply with someParameter().

You can consider your configuration as part of some other container, or context, in which case it's a lot harder to have a slim syntax as you want it, but you necessarily need to inject the context and then call context.someParameter() from your code. I wouldn't recommend making this context class a base class for other objects as it definitely violates separation of concerns and the OOP rule that if Foo is not a Context it shouldn't extend from it. Note that this is basically the first option you describe.

I should add that you can define members in interfaces only if they are static and final. This should not come as a surprise as java does not support multiple inheritance or "mixins". This was a very deliberate choice on the Java language specification team and I believe it was a good one. What you are trying to do does not really fit with the concept of an interface that you can magically extend and get all the members from, unless you used java 8 and defined default methods that rely on some map being read from a file or something, e.g.,

interface Config {
  Map<String, String> getProperties();
  default String someProperty() {
    return getProperties().get("some-prop");
  }
  // ... more utility methods follow
}

The interface above defines a bunch (only one shown) of utility methods returning string properties (it can be any value really) keyed by strings. At startup, you could inject the map (read by a file for example) into your class(es) and have all those utility methods magically defined for you.

The code for a class using this approach would look like:

class MyExperiment implements Config {
  private final Map<String, String> props;
  MyExperiment(Map<String, String> props) { this.props = props; }

  void someMethod() {
    // read property from injected configuration
    String someProp = someProperty();
  }
}

This might save you some typing but will still require that your classes implement Config and define the getProperties() method, which IMO is a violation of separation of concerns and should be avoided.

Giovanni Botta
  • 9,626
  • 5
  • 51
  • 94
  • As I described in the comments for @Machina, they are not constants in Java, but constants for each dataset which this word leaded a misunderstanding. Also about creating a basic class, I agree and that is why I did not mention it a solution; I wrote it just to collect the options. Nevertheless, I should also had written the not-recommended import-option and thank you for considering it. – hossayni Apr 07 '15 at 06:54
1

One of the great benefit of Java is, that it does not provide global variables or constants. The reason behind this is that global stuff makes every other module/class a possible dependant of that stuff. This again makes your design strongly coupled and difficult to maintain.

So what you need to do is first of all to group your variables/constants so that it makes sense. "Container" is obviously no good name for such a group. Try not to go for variable-containers, instead try to find an already existing class which already holds methods that support the same concept as your variables/constants. See java.lang.Math for a good example on this. In your case this could be the repository-class you use for loading the dataset.

Next you should investigate why you would try to access those variables/constants from outside of that class. Accessing (lots of) variables/constants from other classes is usually an indication of bad design. Try to group your classes based on responsibilities and you will probably find a solution where only those variables/constants are only accessed from within the class they reside in.

So basically, if have the feeling that you need to get rid of hundreds of times the container name + '.', you should look for a better design, which either makes the mentioning of the container name a good and necessary information or makes it unnecessary to access those variables/constants from other classes.

Sebastian
  • 877
  • 1
  • 9
  • 20
  • Thanks for your useful answer. By putting in container, I did not mention creating a special class for that and I indeed already using them in the appropriate existing class. – hossayni Apr 07 '15 at 06:58