2012-06-07 26 views
12

Desidero rappresentare una richiesta di URL del servizio Web come oggetto e ho scoperto che ci sono molti parametri comuni che potrebbero essere "ribolliti" in una gerarchia di ereditarietà. Una richiesta potrebbe avere molti parametri, alcuni obbligatori e altri facoltativi, per i quali credo che il pattern di Bloch's Builder sia una buona opzione, che emula argomenti con un'interfaccia fluente.Modello di builder con ereditarietà

In particolare, sto progettando per il servizio Web API di Google Maps, che ha come richiesta del servizio generale web

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

service e output sono argomenti obbligatori, e sensor un parametro obbligatorio. C'è anche un parametro opzionale language.

Ogni servizio ha il suo set di parametri obbligatori e opzionali. Il servizio di geolocalizzazione ha due parametri opzionali, bounds e region. Dispone inoltre di parametri obbligatori mutuamente esclusivi, address o location, che specificano il tipo di servizio (geocodifica diretta o inversa, rispettivamente). Rappresento questa mutua esclusione con nuove classi di bambini.

Immagino che la gerarchia di classe in quanto tale:

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

Poi, mi piacerebbe fare qualcosa di simile al seguente:

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

Come posso creare un cantiere che utilizza argomenti e metodi da la classe e le superclassi in cui è inserita?

risposta

17

Sto costruendo la mia risposta su https://stackoverflow.com/a/9138629/946814, ma considerando questa gerarchia multilivello.

Ciò di cui abbiamo bisogno è di replicare la stessa gerarchia con le classi interne del Builder. Poiché vogliamo concatenare il metodo, abbiamo bisogno di un metodo getThis() che restituisca l'oggetto foglia della gerarchia. Per passare il suo tipo verso l'alto la gerarchia, le classi genitore hanno un generico T e la foglia lega T a se stessa.

Assicura la sicurezza del tipo ed evita qualsiasi lancio di eccezione dovuto a parametri o errori di battitura non inizializzati, oltre alla bella interfaccia fluente. Tuttavia, è un design molto costoso e complesso rappresentare una struttura così semplice come un URL. Spero che sia utile a qualcuno - preferisco la concatenazione di stringhe alla fine.

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

L'implementazione sovrascritta di 'getThis()' nelle classi concrete non dovrebbe essere astratta. – Eric

+0

@EricTobias Hai ragione, un pericolo di copia-incolla. Fisso. –

+1

Questo è fantastico! Esattamente quello che stavo cercando! – Maddy

Problemi correlati