I have some properties in a property file that I referenced in a singleton so that I can access them easily from different parts of my application. For each property, I put the properties along with some additional information for each property in a list of objects. I'm doing this in the singleton's private constructor. Is this the correct way to store/access static data on a server?
Any help or guidance is appreciated.
Added example of what I'm currently doing
public class PropertiesSingleton {
private static PropertiesSingleton instance = null;
private List<MyPropertyObject> properties = new ArrayList<>();
private static PropertyManager pm = PropertyManager.getInstance();
private PropertiesSingleton() {
properties.add("first property description",
"other property info",
new MyPropertyObject(pm.getProperty("firstProperty")));
properties.add("second property description",
"other property info",
new MyPropertyObject(pm.getProperty("secondProperty")));
properties.add("third property description",
"other property info",
new MyPropertyObject(pm.getProperty("thirdProperty")));
}
public static getInstance() {
if (instance == null) {
instance = new PropertiesSingleton();
}
return instance;
}
public getProperties() {
return this.properties;
}
}
2 Answers 2
If it works for you, then in that sense it is correct.
In a broader sense, I think a lot of people would see it as a kind of anti-pattern if you were using it in the following fashion:
class Foo
{
public void Bar()
{
int value = StaticData.getInstance().getUsefulPropertyValue();
// do stuff with value
}
}
This is considered an anti-pattern because the fact that Foo
depends on StaticData
is hidden within Foo
's implementation. This tightly couples Foo
and StaticData
. In this specific situation it may not be that big of a problem because StaticData
is unchanging, but in the general situation this makes it harder to test the parent class.
For example, if Foo
was actually depending on the singleton DatabaseAccess
then unit testing Foo
would be impossible. Foo
would require the presence of a database to be tested which is a) inconvenient, and b) no longer technically a unit test!
You can get around this by using Dependency Injection:
class Foo
{
private DatabaseAccess databaseAccess;
// Here we use Constructor Injection to inject Foo's dependencies into it.
// NOTE: The dependency on DatabaseAccess is made explicit by being a part of Foo's constructor signature.
// This makes it clear that Foo cannot be created without an instance of DatabaseAccess. It therefore depends on it.
public Foo(DatabaseAccess databaseAccess)
{
this.databaseAccess = databaseAccess;
}
public void Bar()
{
// NOTE: We are no longer plucking its dependency out of thin air!
int data = this.databaseAccess.getTheCoolestIntegerEver();
}
}
Functionally, this code is very similar, but in terms of testability and maintainability it is far superior.
Also, note that DatabaseAccess
may still very well be a singleton. The key take-away here is to never access your singletons with a static getInstance()
method.
-
I added an example of what I'm currently doing. Apologies for not doing that in the first place. Hopefully this gives you a better idea of why I'm thinking there's probably a better way to go about doing this than what I'm currently doing.qbolt– qbolt2017年03月17日 17:03:24 +00:00Commented Mar 17, 2017 at 17:03
-
@qbolt, add an usage exampleBasilevs– Basilevs2017年04月17日 02:51:48 +00:00Commented Apr 17, 2017 at 2:51
What is variability of those properties? Are they supposed to differ between dev/test/prod environments?
If the answer is "Yes" - I'd pull those values from environmental variables. This would let you reuse the compiled artifact in all environments. Why use OS environmental vars, not JVM params? E.g. it is useful when packing your artifact inside docker image.
Then, have a look at HOCON config library from Lightbend. It lets you use environment variables and set sensible defaults stored in JSON-like structured and readable form in resources/application.conf.
At last, if your properties can be characterised as config, I usually prefer to let a single entrance for config data into my application. It enters where your app is wired, being validated by some config factory, and then relevant portions of config data are passed down the object graph via constructor parameters.
PropertiesSingleton
.