2012-02-03 27 views
8

supponga di creare una persona nomi di classi utilizzando il builder, e supponiamo che la classe Builder contiene i metodi body(), head(), arms() e naturalmente build() e prendere in considerazione metodi head() e build() obbligatorio per l'utente di questa classe.Come contrassegnare un metodo obbligatorio?

Vorremmo in qualche modo contrassegnare questi metodi obbligatori, se possibile usando annotazioni. Se un utente di questa classe prova a creare un'istanza Person ma ha dimenticato di chiamare uno di questi metodi, vorremmo ricevere qualche tipo di avviso - sia dal compilatore java, sia da Eclipse o Maven, che usiamo per costruire il nostro progetti - ognuno di loro farebbe.

È possibile fare? Quale strada suggeriresti?

+0

Dubbi che puoi farlo in fase di compilazione, tranne che in * molto * casi speciali. Dovrebbe essere abbastanza facile avere controlli come questo in fase di esecuzione però (lo faccio sempre). – NPE

+1

Non riesco ad immaginare un modo per fare questo, quindi aggiungere proprietà obbligatorie come argomenti al costruttore di builder o lanciare un'eccezione quando viene chiamato 'build', ma sono curioso di sapere se qualcuno ha un'idea migliore. –

risposta

15

Ecco un esempio con l'utilizzo di tipi diversi di fare alcune parti obbligatorie (ma rende anche l'ordine di chiamare i metodi obbligatori):

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; 
    } 
} 


Modifica

L'OP era così colpito da questa risposta che lo ha scritto completamente in un blog. È una visione così intelligente del modello di costruzione che merita di essere citato qui un trattamento completo.

+0

Questo è un approccio molto interessante! Ma può essere fatto senza forzare l'ordine, io. e. con interfacce come Mandatory e NonMandatory? – uzilan

+0

No devi forzare l'ordine perché in ogni interfaccia hai bisogno di un metodo che ritorni al passo successivo, quindi non appena metti diversi metodi in una delle interfacce ti costringerai solo a chiamarne uno per poter navigare il passo successivo ... – pgras

+0

Ma potresti aggiungere ai metodi delle interfacce che non aggiungono la proprietà (ad esempio 'PersonBuilder.newInstance(). head (" head "). doesntHaveABody(). arm (" arm ")' – yannick1976

1

Impossibile con il compilatore.

si può fare è lanciare un'eccezione di runtime dal metodo build() che il costruttore non è correttamente inizializzata (e avere un test che viene richiamata in fase di test Maven)

Ma si può anche avere build(..) accettare una HeadDetails oggetto. In questo modo tou non può invocare build senza specificare i parametri obbligatori.

0

Forse all'interno di build() è possibile verificare se sono stati chiamati tutti i metodi richiesti. In breve, l'istanza Person ha un controllo interno di integrità che viene attivato da build().

Ovviamente questo controlla il comportamento di runtime e non è un'analisi statica come la descrivi.

0

non è possibile chiamare questi metodi nel costruttore di Person?

+1

Una delle idee con il modello di builder è che l'utente può scegliere i metodi in Builder che desidera chiamare. Un altro è evitare i costruttori con molti argomenti e molti sovraccarichi (che coprono tutti i modi possibili per creare questo oggetto). Ho paura di ciò che suggerisci potrebbe rompere questi benefici. Vorremmo mantenere la libertà dell'utente di creare l'oggetto Person con qualsiasi metodo di cui hanno bisogno, ma allo stesso tempo affermare che sono chiamati alcuni metodi obbligatori, preferibilmente in fase di costruzione. – uzilan

1

Perché non chiamare body(), head(), arms() nel build() - Metodo se è davvero obbligatorio e restituisce Person nel metodo build()?

[modifica]

breve esempio:

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

btw. se il corpo, la testa e le braccia hanno bisogno di alcuni parametri e sono anche obbligatori, quindi metterli nel costruttore del costruttore. – ollins

+0

Questi metodi richiedono argomenti che dovrei inviare al metodo build(). Questo è contro il modello di costruzione come ho capito. – uzilan

3

Credo che il corretto utilizzo del builder avrebbe risolto il problema riscontrato.

Vorrei creare la classe PersonBuilder che conterrebbe i metodi setBody() e setArms() e ogni altro metodo di impostazione parametri opzionale. Il costruttore del costruttore avrebbe preso i parametri richiesti. Quindi il metodo build() restituirebbe la nuova istanza di 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); 
    } 
} 

In alternativa si potrebbe passare il parametro al metodo Headbuild() ma preferisco passarlo nel costruttore, invece.

Problemi correlati