Map from object to a dynamic string
Right now I have about 60 Message
types which are passed to a getStuff(Message)
method of a class which implements ContainerOfThings
. There are multiple variations of an ContainerOfThings
such as BoxOfStuff
and BagOfTricks
both of which realize the getStuff(Message)
method which generates a string based on member variables. The result may also have pre-pended or post-pended data such as labels or concatenated data. See code below.
public class BoxOfStuff implements ContainerOfThings
{
private String var1;
private String var2;
private String varN;
public String getStuff(Message message)
{
if (message.equals(Message.GET_STUFF1))
return var1;
else if (message.equals(Message.GET_STUFF2))
return "Var2 is: " + var2;
else if (message.equals(Message.GET_STUFFN))
return varN + "\n";
// Etc. for each Message.GET_*
}
// getters and setters for each var*
}
public class Message
{
private String id = null;
private Message(String id)
{ this.id = id; }
public final String toString()
{ return this.id; }
public static final Message GET_STUFF1 = new Message("V1");
public static final Message GET_STUFF2 = new Message("V2");
public static final Message GET_STUFFN = new Message("VN");
}
I am trying to find a design that meets the following objectives. (1) The string returned from getStuf() needs to reflect the current state of the implementing class's member fields. (2) Also I would prefer to get away from an incredibly long series of if / else if
blocks. One concern is ease of potentially changing to a persistent data-driven configurable object approach which a Map
lends well towards. (3) Design should allow for simple maintenance and/or edits.
One design that could work but is a little messy is to create a Map with all key/values initialized in the constructo开发者_如何学Gor and also reset any key/value pair inside each setter method. In this way, the response to getStuff(Message)
is updated to the new content after changes (ie: in a setVar*()
method). Any other thoughts?
I think you'll need two maps. One will be a Map<Message, String>
where the value will be a format string (i.e. something that will get passed into String.format()
). The second map will be a Map<Message, Field>
which should be fairly self explanatory once you take a look at the reflection libs. These will need to be setup at init time but after that the getStuff()
method should be fairly clean and your setters won't be affected at all.
BTW, Java doesn't generally prefix interfaces with I
.
I'm not 100% sure I understand your problem, but it sounds like you want to memoize the result of your getStuff()
call.
One easy way to do this is to use the makeComputingMap()
method from the MapMaker
class in the Google Guava library.
For example, you could do:
Map<Message, String> map = new MapMaker()
.expireAfterWrite(10, TimeUnit.MINUTES)
.makeComputingMap(
new Function<Message, String>() {
public String apply(Message message) {
// Your getStuff() implementation here
}
});
Does that make sense?
How about this:
public abstract class BaseOfBox implements IContainerOfThings {
protected final Map<Message, String> stuffs =
new HashMap<Message, String>();
public final String getStuff(Message message) {
return stuffs.get(message);
}
}
public class BoxOfStuff extends BaseOfBox {
private String var1;
private String var2;
public BoxOfStuff() {
super();
}
public setVar1(String var1) {
this.var1 = var1;
stuffs.put(Message.GET_STUFF1, var1);
}
public setVar2(String var2) {
this.var2 = var2;
stuffs.put(Message.GET_STUFF2, "Var2 is: " + var2);
}
...
}
Frankly, I think this is a pretty ugly solution, but so are the requirements, IMO. I suspect a more elegant solution can only be found if we review the (real) requirements
精彩评论