1

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 constructor 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?

6
  • To clarify: you want to create a Map<Message,String> where String formats the output? Commented Nov 15, 2010 at 23:00
  • The String should contain the current value of any referenced member variable (current as of the time that getStuff() is called) and have any pre-pended or post-pended string data. Commented Nov 15, 2010 at 23:03
  • Does the formatting of the result string for a Message type vary accross the IContainerOfThings implementations? Commented Nov 15, 2010 at 23:16
  • Yes the format may vary depending on the implementing class. Commented Nov 15, 2010 at 23:45
  • Is Message an enum or a class? Show the definition of Message. Commented Nov 16, 2010 at 0:33

3 Answers 3

1

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.

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

1 Comment

I tried out an implementation similar to this one today. Reflection accomplishes the desired effect of having a Map that outputs dynamic strings. The only potential maintenance problem I see is mismatches created when field names are refactored. I'll do some reading and see if I can find a good way to handle that.
1

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?

1 Comment

Definitely interesting... I like it but it seems like series of if /else if statements would just move to the body of the Function declaration in order to populate the cache.
0

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

4 Comments

I was hoping to find a design where (1) Computation of the getStuff response could be performed on-demand; and (2) Get away from the O(n) if / else if series. Yours certainly accomplishes #2.
Well, maybe you just need the Message instances to have an enum field so that getStuff() can use a switch statement.
@Citizen: Your comment clarifies your question considerably; would you mind editing your question to include this? Also, if / else if blocks are not necessarily O(n) -- depending on your tests, the compiler may be able to do better than that. But it's probably a moot point -- unless you have thousands of else if statements, the performance hit will most likely be inconsequential.
@Daniel: I updated the question. Your point about the run time is valid. I realized another reason I try to steer clear of conditional chains. With a map, it is later possible to change the BoxOfStuff to a data-driven configurable object.

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.