In this big legacy project there is a core class MyObject that has an ID property, which has been coded as a String. This ID is accessed everywhere across the project.
public class MyObject {
private String id;
public String getId(){
return id;
}
}
I am looking at the possibility of refactoring this String property to a type class Id with the following methods:
class Id implements Comparable<Id> {
String value
Id(String value)
String getValue()
int hashCode()
boolean equals(Object obj)
int compareTo(Id o)
String toString()
}
When refactoring, I need to keep in mind that while I can refactor our own project in any way, there are customers who use the project's API and the changes should preferably be backwards compatible. The current usages of the String IDs internally are:
equals()
and sometimes with equalsIgnoreCase()
Specifically, what I would like to do is:
String
to Id
String getId()
to Id getUniqueID()
id.equals("String")
or id.equalsIgnoreCase("String")
, change it to id.equals(new Id("String"))
String getId()
that will return getUniqueID().getValue()
. This is for backwards compatibility with customer code that relies on the old String IDs.Of course, I could just list all usages of the property, its getters and setters, and go and replace them by hand. Sometimes, I'd probably be able to get away with using a regex, but it probably isn't that great of an idea. Besides, its plain daunting, as there are 500+ usages to edit across a couple of dozen classes, including sub-classes.
I have looked at IDEA's refactor: type migrate function, but it does not seem to be able to do it. I don't normally work in IDEA, so I might be doing something wrong, but it tells me that there are a few hundred conflicts and the list of things it can't convert is rather long.
It does not look like I can provide a mapping of the old getter getId()
to new getUniqueID().getValue()
, e.g.
myObject.getId().equalsIgnoreCase("test")
would need to map to
myObject.getUniqueID().getValue().equals(new Id("test"))
I imagine that this sort of refactoring should be rather popular, but so far it looks like I will have to do it mostly by hand and search+replace.
Is there an automated way of doing it? Perhaps, some refactoring tool that would allow to specify how old usage pattern would map into new usage pattern?
I think the general answer to the posed question is indeed to use IntelliJ IDEA's functionality: Refactor → Type migration.
The challenge is that it will probably migrate a lot of other variables to the new type, and not just the variable I want to migrate.
For example, we have another class NameDescription that takes two String parameters for name and description and is sometimes used as new NameDescription(myObject.getId(), "Some description")
. In this case, IDEA figures that it probably needs to change NameDescription.name
from String to Id too, which is actually wrong.
In order to avoid this, one needs to preview usages after requesting type migration and manually exclude this and other cases that should not be migrated.
Furthermore, another feature of IDEA can be used to modify patterns in the code: Search/Replace Structurally that allows to define a search pattern with variables and is a bit easier to use than writing a lot of regular expressions yourself.
For backward compatibility, as @c0der suggests, the old getter would return newIdObject.getValue()