2012-02-03 3 views
8

Предположим, вы создаете имена класса Person, используя шаблон строитель, и предположим, что класс Builder содержит методы body(), head(), arms() и конечно build() и вы считаете методы head() и build() обязательным для пользователей данного класса.Как отметить метод обязательный?

Мы хотели бы как-то отметить эти методы обязательными, если возможно, с помощью аннотаций. Если пользователь этого класса пытается создать экземпляр Person, но забыл вызвать любой из этих методов, мы хотели бы получить какое-то предупреждение - либо из java-компилятора, либо, может быть, из Eclipse или Maven, которые мы используем для создания нашего проекты - любой из них будет делать.

Это можно сделать? Каким образом вы бы предложили?

+0

Сомнительно, что вы можете это сделать во время компиляции, за исключением * очень * особых случаев. Должно быть довольно легко иметь такие проверки во время выполнения (я делаю это все время). – NPE

+1

Я не могу представить себе способ сделать это другим, а затем добавить обязательные свойства в качестве аргументов конструктора конструктора или выбросить исключение при вызове 'build', но мне любопытно, есть ли у кого-то идея лучше. –

ответ

15

Ниже приведен пример с использованием различных типов, чтобы сделать некоторые части обязательной (это также делает заказ вы вызываете методы обязательно):

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


Редактировать

ОП было так был впечатлен этим ответом, что он полностью написал его в blog. Это такой умный подход к шаблону строителя, который заслуживает упоминания здесь.

+0

Это очень крутой подход! Но можно ли это сделать, не форсируя заказ, т.е. е. с интерфейсами, такими как Mandatory и NonMandatory? – uzilan

+0

Нет, вам нужно принудительно выполнить заказ, потому что в каждом интерфейсе вам нужен метод, который возвращает следующий шаг, поэтому, как только вы поместите несколько методов в один из интерфейсов, вы заставите только позвонить одному из них, чтобы иметь возможность перейти к следующий шаг ... – pgras

+0

Но вы можете добавить к методам интерфейсов, которые не добавляют свойство (например, 'PersonBuilder.newInstance(). head (" head "). doesntHaveABody(). arm (" arm ")' – yannick1976

1

Ни в коем случае с компилятором.

Вы можете сделать, это выбросить исключение во время выполнения из build() метода, что строитель не правильно инициализирован (и есть тест, который вызывается в тестовом Maven фазе)

Но вы также можете build(..) принять HeadDetails объект. Таким образом tou не может вызывать сборку без указания обязательных параметров.

0

Возможно, внутри build() вы можете проверить, вызваны ли все необходимые методы. Возможно, у экземпляра Person есть внутренняя проверка на работоспособность, которая активируется build().

Конечно, это проверяет поведение во время работы и не является статическим анализом, как вы его описываете.

0

невозможно вызвать эти методы в конструкторе Person?

+1

Одна из идей с шаблоном построителя заключается в том, что пользователь может выбрать, какие методы в Builder она хочет вызвать. Другой способ - избегать конструкторов с множеством аргументов и множеством перегрузок (которые охватывают все возможные способы создания этого объекта). Боюсь, что вы предлагаете разбить эти преимущества. Мы хотели бы сохранить свободу пользователя для создания объекта Person с любыми методами, в которых они нуждаются, но в то же время утверждать, что вызываются некоторые обязательные методы, предпочтительно во время сборки. – uzilan

1

Почему бы не вызвать body(), head(), arms() в build() - метод, если он действительно является обязательным и возвращает Person в методе build()?

[править]

Краткого примера:

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

кстати. если тело, голова и оружие нуждаются в некоторых параметрах, и они также являются обязательными, а затем помещают их в конструктор строителя. – ollins

+0

Эти методы требуют аргументов, которые мне пришлось бы отправить методу build(). Это противоречит шаблону строителя, как я его понимаю. – uzilan

3

Я считаю, что правильное использование шаблона строителя будет решить проблему вы имеете.

Я бы создал класс PersonBuilder, который будет содержать методы setBody() и setArms() и любые другие необязательные методы настройки параметров. Конструктор строителя примет требуемые параметры. Затем метод build() вернет новый экземпляр 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); 
    } 
} 

В качестве альтернативы вы можете передать параметр Head методу build(), но я предпочитаю передать его в конструктор вместо этого.