I have a Java app with three classes: Foo
, Bar
and Baz
. All three depend on a bunch of what are currently constants defined in each class in order to determine how to run. On top of that, Baz
interacts with a number of builder classes that need to be set up in various ways.
I'd like to refactor out all of the configuration info in to a .properties file so that I don't have to recompile every time I change a constant.
But I'm not sure how to structure it:
Do I have a ConfigInfoSingleton
that holds all of the parameters of the application in static properties, and have other classes ask it for information when they need it? That seems wrong, as it introduces global state.
Do I pass around a ConfigInfoMap
as a parameter to all of the classes that need that info? Then I need to have Foo
, Bar
, and Baz
parsing strings in order to decide what they're going to do, and that seems very wrong. Especially in the case of Baz
, where I can't just pass around values, but I need to use switch
statements on configuration info to determine builder method calls.
Do I parse the info in a different class, and have that class tell the others how to set themselves up? Then I have a class that needs to know about everyone else's implementation details, which seems wrong, but less so than the other options.
This seems like a really basic question but I'm not sure how to proceed in terms of good software design, and I haven't had much luck Googling. Whats the "right" thing to do?
-
"Then I need to have Foo, Bar, and Baz parsing strings in order to decide what they're going to do, and that seems very wrong." Parsing strings? Why? You can do all the parsing up front.Doval– Doval2014年07月31日 19:36:30 +00:00Commented Jul 31, 2014 at 19:36
-
@Doval And then pass around enums or something like that?Patrick Collins– Patrick Collins2014年07月31日 19:42:29 +00:00Commented Jul 31, 2014 at 19:42
-
Right. Parse the file, then create an object that represents the configuration file. The object's fields can be enums for config properties that only have a small set of choices.Doval– Doval2014年07月31日 19:45:04 +00:00Commented Jul 31, 2014 at 19:45
-
Okay -- I was vaguely uncomfortable with this idea because it seemed like that would be leaking everyone's implementation details to the configuration file object. But maybe that's not so much of a big deal?Patrick Collins– Patrick Collins2014年07月31日 19:48:51 +00:00Commented Jul 31, 2014 at 19:48
-
Constructor arguments aren't implementation details. Otherwise, every object leaks its implementation.Doval– Doval2014年07月31日 19:52:31 +00:00Commented Jul 31, 2014 at 19:52
3 Answers 3
As Doval mentions you could parse your *.properties
file and create a class that represents your properties and then pass it along to your classes.
A different approach is also to look into a framework that can handle this for you. Spring does not only inject your dependencies but also handles properties very well. This questions gives a good example on how to use Spring together with *.properties
files.
There is also less intrusive libraries available such as Owner that supports live reloading of your properties amongst other things.
-
Upvote for mentioning the Spring approach to the configuration topic.mhaller– mhaller2014年07月31日 22:02:26 +00:00Commented Jul 31, 2014 at 22:02
Parse the config file and create an object representing the config file's values. Then you can decide how to construct Foo
, Bar
, and Baz
based on the the values of the object's fields.
Been there. Done that.
What I did when I had a similar situation was:
Regarding the configuration:
- Created a wrapper around a
Properties
class, I called that wrapperContext
- That wrapper reads and parses the property file
- I created a
ContextDependand
interface with asetContext(Context c)
method - I have
Foo
,Bar
andBaz
implement that interface - I inject that
Context
object into myFoo
,Bar
andBaz
classes so they can acces it without it being a Singleton
Regarding the creation problem:
- I used the
Builder pattern
- I created a Builder interface with just a build method
- Created a FooBuilder, a BarBuilder, a BazBuilder, etc. that implement Builder and return an object which is a superclass of my other classes or an interface they implements
- I added each builder to a HashMap with a key, like
map.put('FOOBUILDER',new FooBuilder())
- Then, instead of a long
case
structure, I just look up the builder in theHashMap
and get it build the object for me, or I traverse the hash and build all of them - That way I tell in the properties files what objects of a collection to build and that can be done without recompiling
If the Builder part is not want you were asking for, at least try the property wrapper / context injection part.