我想将一个Web服务URL请求表示为一个对象,并发现在继承层次结构中可能有很多共同的参数可能被“弄混”。一个请求可以有很多参数,一些是强制性的,而其他则是可选的,我相信Bloch的Builder模式是一个不错的选择,可以使用流畅的接口模拟命名参数。
特别是,我正在设计Google Maps Web服务API,该API具有常规Web服务请求
http://maps.googleapis.com/maps/api/service/output?{parameters}
service
和output
是必选参数,而sensor
是必选参数。还有一个可选参数language
。
每个服务都有其一组必填和可选参数。地理编码服务具有两个可选参数,即bounds
和region
。它还具有互斥的强制性参数address
或location
,它们指定服务的类型(分别为直接或反向地理编码)。我用新的儿童班代表了这种互斥。
我想象这样的类层次结构:
.-----.
| Url |
'-----'
^
|
.---------.
| Request |
'---------'
^
|----------------------------+--------------...
.---------. .------------.
| Geocode | | Directions |
'---------' '------------'
^ ^
|------------+ .
.--------. .---------. .
| Direct | | Reverse | .
'--------' '---------'
然后,我想执行以下操作:
String output = "xml";
boolean sensor = true;
String address = "Av. Paulista, São Paulo, Brasil";
Bounds bounds = new Bounds(-20, -10, -25, -20); //Geographic rectangle
String region = "br";
String lang = "pt-BR";
Coord location = new Coord(-12,-22);
DirectGeocodeRequestUrl direct =
new DirectGeocodeRequestUrl.Builder(output, sensor, address)
.bounds(bounds)
.language(lang)
.build();
ReverseGeocodeRequestUrl reverse =
new ReverseGeocodeRequestUrl.Builder(output, sensor, location)
.language(lang)
.region(region)
.build();
如何创建一个使用来自其插入类和超类中的参数和方法的生成器?
我将答案建立在https://stackoverflow.com/a/9138629/946814上,但考虑到此多层次结构。
我们需要使用Builder内部类复制相同的层次结构。当我们想要方法链接时,我们需要一个getThis()
方法来返回层次结构的叶子对象。为了将其类型向上传递给层次结构,父类具有通用的T
,并且叶子将T
绑定到自身。
它可确保类型安全,并避免由于未初始化的强制性参数或错字以及漂亮的流畅接口而引发任何异常。但是,将URL表示为简单的结构是非常昂贵且复杂的设计。我希望它对某人有用-我更喜欢最后的字符串连接。
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...
}