2012-06-07 14 views
12

Quiero representar una solicitud de URL de servicio web como un objeto, y encontré que hay muchos parámetros comunes que podrían "borrarse" en una jerarquía de herencia. Una solicitud podría tener muchos parámetros, algunos obligatorios y otros opcionales, para los que creo que el patrón de Bloch's Builder es una buena opción, emulando argumentos con nombre con una interfaz fluida.Patrón de generador con herencia

Específicamente, estoy diseñando para la API del servicio web de Google Maps, que tiene como solicitud de servicio web general

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

service y output son argumentos obligatorios y sensor un parámetro obligatorio. También hay un parámetro opcional language.

Cada servicio tiene su conjunto de parámetros obligatorios y opcionales. El servicio Geocode tiene dos parámetros opcionales, bounds y region. También tiene parámetros obligatorios mutuamente excluyentes, address o location, que especifican el tipo de servicio (geocodificación directa o inversa, respectivamente). Represento esta exclusión mutua con nuevas clases para niños.

me imagino la jerarquía de clases como tal:

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

Entonces, me gustaría hacer algo como lo siguiente:

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

¿Cómo puedo crear un constructor que utiliza argumentos y métodos de la clase y las superclases en las que se inserta?

Respuesta

17

Estoy construyendo mi respuesta en https://stackoverflow.com/a/9138629/946814, pero teniendo en cuenta esta jerarquía de niveles múltiples.

Lo que necesitamos es replicar la misma jerarquía con las clases internas del generador. Como queremos el encadenamiento de métodos, necesitamos un método getThis() que devuelva el objeto hoja de la jerarquía. Para pasar su tipo hacia arriba de la jerarquía, las clases principales tienen un genérico T, y la hoja se une a T.

Asegura la seguridad de tipo y evita cualquier excepción lanzada debido a los parámetros obligatorios no inicializados o errores tipográficos, además de la interfaz fluida agradable. Sin embargo, es un diseño muy costoso y complejo para representar una estructura tan simple como una URL. Espero que sea útil para alguien, prefiero la concatenación de cadenas al final.

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

La implementación anulada de 'getThis()' en las clases concretas no debe ser abstracta. – Eric

+0

@EricTobias Tienes razón, un peligro de copiar y pegar. Fijo. –

+1

¡Esto es asombroso! ¡Exactamente lo que estaba buscando! – Maddy

Cuestiones relacionadas