2

I'm considering refactoring some Java code that passes around objects that implement the interface Map<String, Object>. The strings are all (as far as I know) from some fixed list of string literals. But as I said, I don't know which particular strings are used. I think it would be better if the Maps used an enum for the keys instead so that we get more type safety and the possibilities are forced to be listed in the source code.

The Maps are used in enough places that it is not practical to change all that code at once. If it's ever going to happen, it needs to happen gradually. How can this change be performed one section of code at a time, while still leaving the code in a working state?

1
  • enum.ToString()
    – Ewan
    Commented Sep 3, 2019 at 7:59

2 Answers 2

4

If you really need to introduce this gradually, you will temporarily have two kind of maps in your program: one Map<String, Object> type, and one Map<MyEnum, Object> type. A conversion from the latter to the former is straight-forward, but a conversion from the former to the latter may not be possible as long as MyEnum does not contain all the required keys.

So I guess your best bet is to identify subsections or classes in your code where you can introduce Map<MyEnum, Object> in isolation, and where the conversion of Map<String, Object> into Map<MyEnum, Object> is not required, whilst the reverse conversion does not result in an unacceptable performance hit. Just call a conversion function at the borders between the already refactored code and the unrefactored parts, as long as the refactoring is not complete.

Since we do not know the structure of your actual code, I guess there is not much more which can be recommended here, and how viable this approach is. Of course, there is always the standard recommendation for such a kind of refactoring - provide enough automated regression tests before you start. Note also that once you change the type of Map<String, Object> to Map<MyEnum, Object> for one variable, the compiler will tell you if you forgot to refactor a place in you code where now either MyEnum is required, or a conversion between those types. So I guess the refactoring is relatively safe and not too error-prone.

You can actually utilize the compiler error messages and unit tests for making different attempts for partial refacorings, and, whenever an attempt fails, undo all changes in source control and start with a different part of the codebase again. Ola Ellnestam and Daniel Brolund made a systematic approach of this refactoring technique and called it "The Mikado Method". I haven't read their book, so I cannot tell you if it is worth the price, but maybe the general idea is sufficient for you case.

2

Your other option is to use a value object, combined with exposed properties representing your enum values.

  1. Create a value object that wraps your string value
  2. Overload your value object casting so you can cast between a value object and its string value (you should be able to use a value object anywhere a string can be used and vice versa).
  3. Expose constant or static members from your value object with your desired future enum names and values (ValueObject.String1)
  4. As you have time, convert each section of code to use the exposed properties, instead of the underlying string values if (stringValue.Equals("string#1") would become if(stringValue.Equals(ValueObject.String1)
  5. Once you are confident that you have changed an adequate amount of code, replace the value object with a true enum, using the same exposed property names of the value object

That should avoid breaking changes, while also gradually moving you closer to an enum solution. The casting on the value object will let your changes work with legacy code, while the exposed properties let you slowly refactor towards an enum solution.

2
  • If I search for value objects and overloading casting I get pages like this one stackoverflow.com/q/23036546 that seem to indicate that the automatic conversion can’t be made to work in Java. Still, useful to know about for C#.
    – Ryan1729
    Commented Sep 3, 2019 at 15:18
  • Good find on that question; I admit my Java knowledge is peripheral at best. I assumed there might be an equivalent or a workaround in Java. Commented Sep 3, 2019 at 16:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.