Assume there's a public void process
method on a Java class called A
that currently takes a single String
argument id
.
class A {
public void process(final String id) {
// Some implementation
}
}
The existing method's String
id
argument has a specific format: type#integer
where type
can be foo
or bar
(so foo#1
and bar#1
).
While the underlying implementation does not change based on the format of id, having the method take a String
seems less accurate than representing the argument as a concrete type, especially when unfamiliar readers examine the public interface of the class.
One approach is to parse the id
value into one of two classes (FooId
and a BarId
) and to represent the id
argument as the Union
of two input types - a FooId
and a BarId
.
public class FooId {
}
public class BarId{
}
public class IdParser {
public Union<FooId, BarId> parse(final String id) {
final String parts = id.split("#");
if (2 == parts.length) {
final int value = Integer.parseInt(parts[1]);
if ("foo".equals(parts[0])) {
return Union._1(new FooId(value));
}
if ("bar".equals(parts[0])) {
return Union._2(new BarId(value));
}
}
throw new IlllegalArgumentException("Invalid id value");
}
}
public void process(final Union2<FooId, BarId> id)
Another approach is to represent the id
value as a "generic" Id
class that wraps the two types and keeps track of the type based on an internal Type
enum
like
class Id {
private static final enum Type {
Foo,
Bar;
}
private final Type type;
private final int value;
private Id(final Type type, final int value) {
this.type = type;
this.value = value;
}
public static Id from(final String formattedId) {
// parse the type and value and return an Id instance
}
}
What are the tradeoffs between these two approaches?
Would you instead recommend using method overloading instead of a Union
?
public void process(final FooId id)
public void process(final BarId id)
String
values have two different prefixes. One could make the argument that these two prefixes represent two different types of ids where thefoo
-prefixed idString
values can be represented by aFooId
class and thebar
-prefixed idString
values can be represented by aBarId
class.foo
andbar
prefixes - let's say the prefixes arecar
andmotorcycle
(so the inputs could look likecar#1234
andmotorcycle#1234
). You could imagine that instead of expressingcar#1234
as aString
, you could create a new classCarId
with aprivate int
membervalue
variable (1234
, for example). You could do the same thing withmotorcycle#1234
andMotorcycleId
. So now, instead of an input where theString
has two different formats, there are concrete classes that can express the different input forms.