2012-02-03 20 views
8

supongamos que crea una Persona nombres de clase utilizando el Builder, y supongamos que la clase contiene métodos constructor body(), head(), arms() y por supuesto build() y considere métodos head() y build() obligatorio para el usuario de esta clase.¿Cómo marcar un método obligatorio?

Nos gustaría de alguna manera marcar estos métodos como obligatorios, de ser posible usando anotaciones. Si un usuario de esta clase intenta crear una instancia de Persona pero olvidó llamar a cualquiera de estos métodos, nos gustaría obtener algún tipo de advertencia, ya sea del compilador de Java, o tal vez de Eclipse o Maven, que usamos para construir nuestro proyectos - cualquiera de ellos lo haría.

¿Es posible hacer? ¿De qué manera sugerirías?

+0

Duda que puede hacer esto en tiempo de compilación, excepto en * muy * casos especiales. Sin embargo, debería ser bastante fácil tener controles como este en tiempo de ejecución (lo hago todo el tiempo). – NPE

+1

No me puedo imaginar una manera de hacer esto aparte de agregar propiedades obligatorias como argumentos al constructor del constructor o lanzar una excepción cuando se llama a 'build', pero tengo curiosidad de que alguien tenga una idea mejor. –

Respuesta

15

Aquí se muestra un ejemplo con el uso de diferentes tipos para hacer algunas partes obligatoria (que también hace que la orden se llama a los métodos obligatorios):

package test; 

import test.StepOne.StepThree; 
import test.StepOne.StepTwo; 
import test.StepOne.LastStep; 

public class TestBuilder { 

    public static void main(String[] args) { 

     String person1 = PersonBuilder.newInstance().head("head").body("body").arm("arm").leg("leg").build(); 

     String person2 = PersonBuilder.newInstance().head("head").body("body").arm("arm").build(); 

    } 

} 

interface StepOne { 

    // mandatory 
    StepTwo head(String head); 

    interface StepTwo { 
     // mandatory 
     StepThree body(String body); 
    } 

    interface StepThree { 
     // mandatory 
     LastStep arm(String arm); 
    } 

    // all methods in this interface are not mandatory 
    interface LastStep { 
     LastStep leg(String leg); 
     String build(); 
    } 

} 

class PersonBuilder implements StepOne, StepTwo, StepThree, LastStep { 

    String head; 
    String body; 
    String arm; 
    String leg; 

    static StepOne newInstance() { 
     return new PersonBuilder(); 
    } 

    private PersonBuilder() { 
    } 



    public StepTwo head(String head) { 
     this.head = head; 
     return this; 
    } 

    public LastStep arm(String arm) { 
     this.arm = arm; 
     return this; 
    } 

    public StepThree body(String body) { 
     this.body = body; 
     return this; 
    } 

    public LastStep leg(String leg) { 
     this.leg = leg; 
     return this; 
    } 

    public String build() { 
     return head + body + arm + leg; 
    } 
} 


Editar

El PO fue tan impresionado con esta respuesta que lo escribió por completo en un blog. Es una versión tan inteligente del patrón de construcción que un tratamiento completo merece ser referenciado aquí.

+0

¡Es un enfoque genial! Pero puede hacerse sin forzar el orden, i. mi. con interfaces como Mandatory y NonMandatory? – uzilan

+0

No tiene que forzar el pedido porque en cada interfaz necesita un método que devuelve el siguiente paso, de modo que tan pronto como coloque varios métodos en una de las interfaces, solo tendrá que llamar a uno de ellos para poder navegar hasta el siguiente paso ... – pgras

+0

Pero podría agregar a los métodos de interfaz que no agregan la propiedad (por ejemplo, 'PersonBuilder.newInstance(). head (" head "). doesntHaveABody(). arm (" arm ")' – yannick1976

1

De ninguna manera con el compilador.

que puede hacer es lanzar una excepción de ejecución del método build() que el constructor no se ha inicializado correctamente (y tener una prueba de que se invoca en la fase de prueba experta)

Pero también puede tener build(..) aceptar una HeadDetails objeto. De esa forma tou no puede invocar build sin especificar los parámetros obligatorios.

0

Tal vez dentro de build() se puede verificar si se han llamado a todos los métodos requeridos. Tal vez la instancia Person tiene algún control de cordura interno que se desencadena por build().

Por supuesto, esto comprueba el comportamiento del tiempo de ejecución y no es un análisis estático como usted lo describe.

0

no es posible llamar a estos métodos en el constructor de persona?

+1

Una de las ideas con el patrón de generador es que el usuario puede elegir qué métodos de Builder le gustaría llamar. Otra es evitar constructores con muchos argumentos y muchas sobrecargas (que cubren todas las formas posibles de crear este objeto). Me temo que lo que sugieres puede romper estos beneficios. Nos gustaría conservar la libertad del usuario para crear el objeto Persona con cualquier método que necesite, pero al mismo tiempo afirmar que se invocan algunos métodos obligatorios, preferiblemente en tiempo de compilación. – uzilan

1

¿Por qué no llamar a body(), head(), arms() en la construcción() - Método si es realmente obligatorio y devolver Person en el método build()?

[editar]

ejemplo corto:

public class Builder { 

private final String bodyProp; 

private final String headProp; 

private final String armsProp; 

private String hearProps; 

public Builder(String bodyProp, String headProp, String armsProp) { 
    super(); 
    this.bodyProp = bodyProp; // check preconditions here (eg not null) 
    this.headProp = headProp; 
    this.armsProp = armsProp; 
} 

public void addOptionalHair(String hearProps) { 
    this.hearProps = hearProps; 
} 

public Person build() { 
    Person person = new Person(); 

    person.setBody(buildBody()); 
    // ... 

    return person; 
} 



private Body buildBody() { 
    // do something with bodyProp 
    return new Body(); 
} 


public static class Person { 

    public void setBody(Body buildBody) { 
     // ... 
    } 
} 

public static class Body { 
} 
} 
+0

por cierto. si el cuerpo, la cabeza y los brazos necesitan algunos parámetros y también son obligatorios, póngalos en el constructor del constructor. – ollins

+0

Estos métodos requieren argumentos que tendría que enviar al método build(). Esto va en contra del patrón del constructor tal como lo entiendo. – uzilan

3

Creo que el uso correcto del Builder resolvería el problema que está teniendo.

Crearía la clase PersonBuilder que contendría los métodos setBody() y setArms() y cualquier otro método de configuración de parámetros opcional. El constructor del constructor tomaría los parámetros requeridos. Entonces el método build() devolvería la nueva instancia de Person.

public class PersonBuilder 
{ 
    private final Head head; 
    private Body body; 
    private Arms arms; 

    public PersonBuilder(Head head) 
    { 
     this.head = head; 
    } 

    public void setBody(Body body) 
    { 
     this.body = body; 
    } 

    public void setArms(Arms arms) 
    { 
     this.arms = arms; 
    } 

    public Person build() 
    { 
     return new Person(head, body, arms); 
    } 
} 

alternativa usted puede pasar el parámetro Head al método build() pero prefiero pasar en el constructor en su lugar.

Cuestiones relacionadas