Несколько Конструкторов без значения параметра по умолчанию для некоторых параметров - PullRequest
0 голосов
/ 14 апреля 2020

Если вам нужно создать несколько конструкторов, прямое решение будет заключаться в цепочке конструкторов. Один конструктор вызывает другой с некоторым значением по умолчанию. Но что, если нет значения по умолчанию?

Например, класс с тремя конструкторами:

public MyClass(Person person, SomeEnum enum1) // constructor 1
{

    if (person== null)
    {
        throw new IllegalArgumentException("person cannot be null.");
    }
    if (enum1== null)
    {
        throw new IllegalArgumentException("enum1 cannot be null.");
    }       

    this.person= person;
    this.enum1 = enum1;     
}

public MyClass(Person person)  // constructor 2
{

    if (person== null)
    {
        throw new IllegalArgumentException("person cannot be null.");
    }   

    this.person= person;    
}

public MyClass(SomeEnum enum1)  // constructor 3
{

    if (enum1== null)
    {
        throw new IllegalArgumentException("enum1 cannot be null.");
    }       

    this.enum1 = enum1;     
}

Я не хочу проверять одни и те же поля в нескольких конструкторах и т. Д. Я постараюсь сделать конструкторов 2 и 3 вызовом конструктор 1 :

public MyClass(Person person, SomeEnum enum1) // constructor 1
    {

        if (person== null)
        {
            throw new IllegalArgumentException("person cannot be null.");
        }
        if (enum1== null)
        {
            throw new IllegalArgumentException("enum1 cannot be null.");
        }       

        this.person= person;
        this.enum1 = enum1;     
    }

    public MyClass(Person person)  // constructor 2
    {                   
        this(prerson, SomeEnum.NO_VALUE);
    }

    public MyClass(SomeEnum enum1)  // constructor 3
    {               
        this(?, enum1);
    }

Итак, допустим, у нас есть какое-то значение по умолчанию в SomeEnum , но с конструктором 3 у меня нет значения по умолчанию для Person . Я могу изменить цепочку вызовов и заставить конструкторы 1 и 2 вызывать конструктор 3, а затем проверять и устанавливать Person самостоятельно, но я не хочу, чтобы проверка Person проводилась в двух местах.

Я также не Мне не нравится идея создать какой-либо NotPerson объект, который расширяет Person и использовать его. И модель строителя тоже мне не подходит. Кроме того, проверка может быть более сложной, чем просто проверка на нулевое значение.

Ответы [ 3 ]

3 голосов
/ 14 апреля 2020

Если ваша проблема связана главным образом с дублированием проверок, то Project Lombok может легко решить эту проблему. Он сгенерирует эквивалентный код автоматически. Он использует NullPointerException по умолчанию, но его можно настроить для использования IllegalArgumentException, если хотите.

public MyClass(@NonNull Person person, @NonNull SomeEnum enum1) {
    this.person = person;
    this.enum1 = enum1;     
}

public MyClass(@NonNull Person person) {
    this.person = person;    
}

public MyClass(@NonNull SomeEnum enum1) {
    this.enum1 = enum1;     
}
2 голосов
/ 14 апреля 2020

Один из способов - написать приватные методы проверки c. Компактный способ их использования будет выглядеть следующим образом:

public MyClass(Person person, SomeEnum enum1) {
    this.person = validatePerson(person);
    this.enum1 = validateSomeEnum(enum1);
}
public MyClass(Person person) {
    this.person = validatePerson(person);
}
public MyClass(SomeEnum enum1) {
    this.enum1 = validateSomeEnum(enum1);
}

private static Person validatePerson(Person person) {
    if (person == null) {
        throw new IllegalArgumentException("person cannot be null.");
    }
    return person;
}
private static SomeEnum validateSomeEnum(SomeEnum enum1) {
    if (enum1 == null) {
        throw new IllegalArgumentException("enum1 cannot be null.");
    }
    return enum1;
}

Использование

MyClass myClass = new MyClass(person, enum1);
MyClass myClass = new MyClass(person);
MyClass myClass = new MyClass(enum1);

Вы также можете сделать это с помощью конструкции stati c методы, полезные для уточнения аргументов при наличии неоднозначности, называя метод stati c по-разному:

public static MyClass of(Person person, SomeEnum enum1) {
    validatePerson(person);
    validateSomeEnum(enum1);
    return new MyClass(person, enum1);
}
public static MyClass of(Person person) {
    validatePerson(person);
    return new MyClass(person, null);
}
public static MyClass of(SomeEnum enum1) {
    validateSomeEnum(enum1);
    return new MyClass(null, enum1);
}

private static void validatePerson(Person person) {
    if (person == null) {
        throw new IllegalArgumentException("person cannot be null.");
    }
}
private static void validateSomeEnum(SomeEnum enum1) {
    if (enum1 == null) {
        throw new IllegalArgumentException("enum1 cannot be null.");
    }
}

private MyClass(Person person, SomeEnum enum1) {
    this.person = person;
    this.enum1 = enum1;
}

Использует

MyClass myClass = MyClass.of(person, enum1);
MyClass myClass = MyClass.of(person);
MyClass myClass = MyClass.of(enum1);

Другой вариант - это шаблон Builder, особенно полезный, если есть много дополнительных свойств:

public static final class Builder {
    private Person person;
    private SomeEnum enum1;

    public Builder withPerson(Person person) {
        if (person == null) {
            throw new IllegalArgumentException("person cannot be null.");
        }
        this.person = person;
        return this;
    }
    public Builder withSomeEnum(SomeEnum enum1) {
        if (enum1 == null) {
            throw new IllegalArgumentException("enum1 cannot be null.");
        }
        this.enum1 = enum1;
        return this;
    }
    public MyClass create() {
        if (this.person == null && this.enum1 == null) {
            throw new IllegalArgumentException("One of person or enum1 is required.");
        }
        return new MyClass(this.person, this.enum1);
    }
}

private MyClass(Person person, SomeEnum enum1) {
    this.person = person;
    this.enum1 = enum1;
}

Использует

MyClass myClass = new MyClass.Builder()
        .withPerson(person)
        .withSomeEnum(enum1)
        .create();
MyClass myClass = new MyClass.Builder()
        .withPerson(person)
        .create();
MyClass myClass = new MyClass.Builder()
        .withSomeEnum(enum1)
        .create();
0 голосов
/ 15 апреля 2020

Другим решением было бы лучше изменить цепной вызов:

public MyClass(Person person, SomeEnum enum1) // constructor 1
{

    this(enum1);
    if (person== null)
    {
        throw new IllegalArgumentException("person cannot be null.");
    }   

    this.person= person;   
}

public MyClass(Person person)  // constructor 2
{
    this(person, SomeEnum.NoValue);     
}

public MyClass(SomeEnum enum1)  // constructor 3
{

    if (enum1== null)
    {
        throw new IllegalArgumentException("enum1 cannot be null.");
    }       

    this.enum1 = enum1;     
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...