16

Web サービスの URL リクエストをオブジェクトとして表現したいのですが、継承階層で「バブルアップ」できる共通パラメーターがたくさんあることがわかりました。リクエストには、いくつかの必須およびその他のオプションの多くのパラメーターを含めることができます。そのため、流暢なインターフェースで名前付き引数をエミュレートする Bloch の Builder パターンが優れたオプションであると私は信じています。

具体的には、一般的な Web サービス要求として Google マップ Web サービス API を設計しています。

http://maps.googleapis.com/maps/api/service/output?{parameters}

serviceoutputは必須の引数でありsensor、必須のパラメーターです。オプションのパラメータもありますlanguage

各サービスには、一連の必須パラメータとオプション パラメータがあります。Geocode サービスには、2 つのオプション パラメータが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();

挿入先のクラスとスーパークラスの引数とメソッドを使用する Builder を作成するにはどうすればよいですか?

4

1 に答える 1

19

https://stackoverflow.com/a/9138629/946814に基づいて回答を作成していますが、このマルチレベルの階層を考慮しています。

必要なのは、ビルダーの内部クラスで同じ階層を複製することです。メソッドの連鎖が必要なためgetThis()、階層のリーフ オブジェクトを返すメソッドが必要です。その型を階層の上位に渡すために、親クラスにはジェネリックTがあり、リーフはそれ自体にバインドTされます。

型の安全性を保証し、初期化されていない必須パラメーターやタイプミスによる例外のスローを回避し、流暢なインターフェイスを備えています。ただし、このような単純な構造を URL として表現するには、非常にコストがかかり複雑な設計になります。私はそれが誰かに役立つことを願っています - 私は最後に文字列連結を好みました。

リクエスト URL:

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...
}
于 2012-06-07T23:57:43.433 に答える