I am trying to refactor some legacy code. The task here is to construct lengthy messages/strings based on some pre-defined template that looks like this:
field1,8,String
filed2,5,Integer
field3,12,String
......
Then I am handed a java object that has all those fields. What needs to be done here is simply get data from the object fields and use them to construct a long message/string based on the template. Some of these fields also may be converted based on some simple rules. For example:
abc => a
def => d
ghi => g
As a result we need to check the values of these fields from time to time. Also there are rules about padding (mostly adding empty space to the right). So a conostructed message/string may look like this:
uater 4751 enterprise ......
Currently we are just using brutal force to do this job. First we feed the template into an ArrayList, each element is a line, eg, "field1,8,String". During the actual message construction, we loop through this ArrayList, and then fill the data into a StringBuffer. Here is some sample snippet
StringBuffer message = new StringBuffer(1000);
for (String field : templateFields) {
String[] fieldArray = field.split(Constants.SEPARATOR);
if (fieldArray[0].equalsIgnoreCase(Constants.WORKFLOW)) {
message.append(rightPad(object.getFieldOne(), Integer.parseInt(fieldArray[1])));
} else if (fieldArray[0].equalsIgnoreCase(Constants.WORKVOLUME)) {
message.append(rightPad(object.getFieldTwo(), Integer.parseInt(fieldArray[1]));
} else if (fieldArray[0].equalsIgnoreCase(Constants.WORKTYPE)) {
if (object.getFieldThree().equalsIgnoreCase("abc")) {
message.append(rightPad("a", Integer.parseInt(fieldArray[1]));
} else if (object.getFieldThree().equalsIgnoreCase("def")) {
message.append(rightPad("d", Integer.parseInt(fieldArray[1]));
} else {
message.append(rightPad("g", Integer.parseInt(fieldArray[1]));
}
} else if ......
}
As you can see, as hidious as it is, it gets the job done. But such code is error-prone, and is hard to maintain. I wonder if you guys have any tools or libraries or some elegant solutions to recommend.
Thanks so much! Hua
If I understand your question right, you have an approach where you are looping over possible
templateFields. That's not necessary.Since every
fieldArray[0]is compared to someConstantsvalues and in case of a match is processed further, we can replace the for-loop by aMap. Its keys are the possibleConstantsvalues and its values are mappers. A mapper is aBiFunctionwhich takes theobjectand the value offieldArray[1]and returns for these a message of typeString.Let's start with the mappers:
We do not return a mapper itself.
FieldToMessageMapperoffers the methodmapwhich does the mapping. It returns anOptional<String>which shows the result might be empty if there is no mapping for the input.To ensure to get a mapper independent of the characters case, all keys are
String..toLowerCase().Let's go on with the overall processing:
I don't know how you need to handle missing mappings. I choose fail fast by throwing an exception.
Please note:
StringBufferisIf your processing isn't multithreaded you could use
StringBuilder. If the result isn't modified further, you could useString.Let me show a further alternative using
Streamwhich returns aString:If I got the code from the question right, there should be the following implementation of
Constants:EDIT:
If you want to have a configuration approach you can elaborate this code further to use Spring configuration:
MessageMapperwhich has two methods:String getKey()andString map(MyObject o, String fieldArray1).getKey()returns theConstantsvalue for which the mapper provides the mapping.MESSAGE_MAPPERusing this interface.CommonMessageMapperwhich has a constructorCommonMessageMapper(MessageMapper... messageMappers). ThemessageMappershas to be put in aMap<String, BiFunction<MyObject, String, String>> mapperslike:mappers.put(messageMapper.getKey(), messageMapper). Define a methodString map(MyObject o, String fieldArray0, String fieldArray1)which will lookup the appropriateMessageMapper mmusingfieldArray0:MessageMapper mm = mappers.get(fieldArray0). Invoke thenmm.map(o, feldArray1). (You may use here also anOptionalto handle the case when no appropriate mapper is present.)MessageMapperand theCommonMessageMapperhave to be annotated asBeanorComponent. The constructor ofCommonMessageMapperhas to be annotated with@Autowired.@Configuration) which will inject the desiredMessageMapperinto aCommonMessageMapperand has a factory method for such aCommonMessageMapper.CommonMessageMapperinstead ofFieldToMessageMapperabove.