2012-06-07 15 views
12

Ich möchte eine Web-Service-URL-Anfrage als ein Objekt darstellen, und festgestellt, dass es viele allgemeine Parameter gibt, die in einer Vererbungshierarchie "aufgebläht" werden könnten. Eine Anfrage könnte viele Parameter enthalten, einige obligatorisch und andere optional, für die ich Blochs Builder-Muster als eine nette Option empfinde, die benannte Argumente mit einer fließenden Oberfläche emuliert.Builder-Muster mit Vererbung

Insbesondere ist ich die Gestaltung für den Google Maps Web-Service-API, die als allgemeines Web-Service-Anfrage hat

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

service und output obligatorische Argumente sind, und sensor die obligatorischen Parameter. Es gibt auch einen optionalen Parameter language.

Für jeden Dienst gibt es obligatorische und optionale Parameter. Der Geocode-Dienst verfügt über zwei optionale Parameter: bounds und region. Es hat auch gegenseitig obligatorische Parameter, address oder location, die die Art des Dienstes (direkte oder umgekehrte Geokodierung, jeweils angeben). Ich vertrete diesen gegenseitigen Ausschluss mit neuen Kinderklassen.

Ich stelle mir die Klassenhierarchie als solche:

.-----. 
    | Url | 
    '-----' 
    ^
    | 
.---------. 
| Request | 
'---------' 
    ^
    |----------------------------+--------------... 
.---------.     .------------. 
| Geocode |     | Directions | 
'---------'     '------------' 
    ^      ^
    |------------+    . 
.--------. .---------.   . 
| Direct | | Reverse |   . 
'--------' '---------' 

Dann würde Ich mag so etwas wie das tun folgendes:

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(); 

Wie kann ich einen Builder, die Argumente und Methoden verwendet von die Klasse und Oberklassen, in denen es eingefügt wird?

Antwort

17

Ich baue meine Antwort auf https://stackoverflow.com/a/9138629/946814, aber unter Berücksichtigung dieser mehrstufigen Hierarchie.

Wir müssen die gleiche Hierarchie mit den inneren Builder-Klassen replizieren. Da wir eine Verkettung von Methoden wünschen, benötigen wir eine getThis() Methode, die das Blattobjekt der Hierarchie zurückgibt. Um seinen Typ die Hierarchie nach oben zu übergeben, haben die Elternklassen eine generische T, und das Blatt bindet T an sich.

Es gewährleistet Typ-Sicherheit und vermeidet jede Ausnahme werfen wegen nicht initialisierten obligatorischen Parameter oder Tippfehler, plus die nette fließende Schnittstelle. Es ist jedoch ein sehr kostenintensives und komplexes Design, eine so einfache Struktur wie eine URL darzustellen. Ich hoffe, es ist nützlich für jemanden - ich bevorzuge String-Verkettung am Ende.

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 RequestUrl(Builder 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 GeocodeRequestUrl(Builder 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... 
} 
+0

Die überschriebene Implementierung von 'getThis()' in den konkreten Klassen sollte nicht abstrakt sein. – Eric

+0

@EricTobias Du hast Recht, eine Gefahr von Copy-Paste. Fest. –

+1

Das ist erstaunlich! Genau das, was ich gesucht habe! – Maddy