I want to represent an web service URL request as an object, and found that there are lots of common parameters that could be \"bubbled up\" in an inheritance hierarchy. A reque
I'm building my answer upon https://stackoverflow.com/a/9138629/946814, but considering this multi-level hierarchy.
What we need is to replicate the same hierarchy with the Builder inner classes. As we want method chaining, we need a getThis()
method that returns the leaf object of the hierarchy. In order to pass its type upward the hierarchy, the parent classes have a generic T
, and the leaf binds T
to itself.
It assures type-safety and avoids any exception throwing due to uninitialized mandatory parameters or typos, plus the nice fluent interface. However, it's a very costy and complex design to represent such a simple structure as an URL. I hope it is useful to someone - I preferred string concatenation at the end.
RequestUrl:
public abstract class RequestUrl{
public static abstract class Builder<T extends Builder<T>>{
protected String output;
protected boolean sensor;
//Optional parameters can have default values
protected String lang = "en";
public Builder(String output, boolean sensor){
this.output = output;
this.sensor = sensor;
}
public T lang(String lang){
this.lang = lang;
return getThis();
}
public abstract T getThis();
}
final private String output;
final private boolean sensor;
final private String lang;
protected <T extends Builder<T>> RequestUrl(Builder<T> builder){
this.output = builder.output;
this.sensor = builder.sensor;
this.lang = builder.lang;
}
// other logic...
}
GeocodeRequestUrl:
public abstract class GeocodeRequestUrl extends RequestUrl {
public static abstract class Builder<T extends Builder<T>>
extends RequestUrl.Builder<Builder<T>>{
protected Bounds bounds;
protected String region = "us";
public Builder(String output, boolean sensor){
super( output, sensor );
}
public T bounds(Bounds bounds){
this.bounds = bounds;
return getThis();
}
public T region(String region){
this.region = region;
return getThis();
}
@Override
public abstract T getThis();
}
final private Bounds bounds;
final private String region;
protected <T extends Builder<T>> GeocodeRequestUrl(Builder<T> builder){
super (builder);
this.bounds = builder.bounds;
this.region = builder.region;
}
// other logic...
}
DirectGeocodeRequestUrl:
public class DirectGeocodeRequestUrl extends GeocodeRequestUrl {
public static class Builder<Builder>
extends GeocodeRequestUrl.Builder<Builder>{
protected String address;
public Builder(String output, boolean sensor, String address){
super( output, sensor );
this.address = address;
}
@Override
public Builder getThis(){
return this;
}
public DirectGeocodeRequestUrl build(){
return new DirectGeocodeRequestUrl(this);
}
}
final private String address;
protected DirectGeocodeRequestUrl(Builder builder){
super (builder);
this.address = builder.address;
}
// other logic...
}
ReverseGeocodeRequestUrl:
public class ReverseGeocodeRequestUrl extends GeocodeRequestUrl {
public static class Builder<Builder>
extends GeocodeRequestUrl.Builder<Builder>{
protected Coord location;
public Builder(String output, boolean sensor, Coord location){
super( output, sensor );
this.location = location;
}
@Override
public Builder getThis(){
return this;
}
public ReverseGeocodeRequestUrl build(){
return new ReverseGeocodeRequestUrl(this);
}
}
final private Coord location;
protected ReverseGeocodeRequestUrl(Builder builder){
super (builder);
this.location = builder.location;
}
// other logic...
}