Управление конструкторами со многими параметрами в Java

В некоторых наших проектах есть иерархия классов, которая добавляет больше параметров по мере продвижения по цепочке. Внизу, некоторые из классов могут иметь до 30 параметров, 28 из которых просто передаются в супер-конструктор.

Я признаю, что использование автоматического DI через что-то вроде Guice было бы неплохо, но по некоторым техническим причинам эти конкретные проекты ограничены Java.

Соглашение об организации аргументов в алфавитном порядке по типу не работает, потому что если тип подвергается рефакторингу (круг, который вы передавали для аргумента 2, теперь является формой), он может внезапно выйти из строя.

Этот вопрос может быть конкретным и чреват критикой "Если это ваша проблема, вы делаете это неправильно на уровне дизайна", но я просто ищу любые точки зрения.

8 ответов

Решение

Шаблон проектирования Builder может помочь. Рассмотрим следующий пример

public class StudentBuilder
{
    private String _name;
    private int _age = 14;      // this has a default
    private String _motto = ""; // most students don't have one

    public StudentBuilder() { }

    public Student buildStudent()
    {
        return new Student(_name, _age, _motto);
    }

    public StudentBuilder name(String _name)
    {
        this._name = _name;
        return this;
    }

    public StudentBuilder age(int _age)
    {
        this._age = _age;
        return this;
    }

    public StudentBuilder motto(String _motto)
    {
        this._motto = _motto;
        return this;
    }
}

Это позволяет нам писать код как

Student s1 = new StudentBuilder().name("Eli").buildStudent();
Student s2 = new StudentBuilder()
                 .name("Spicoli")
                 .age(16)
                 .motto("Aloha, Mr Hand")
                 .buildStudent();

Если мы пропустим обязательное поле (предположительно, имя обязательно), то мы можем сделать так, чтобы конструктор Student выбросил исключение. И это позволяет нам использовать стандартные / необязательные аргументы без необходимости отслеживания любого вида порядка аргументов, так как любой порядок этих вызовов будет работать одинаково хорошо.

Можете ли вы инкапсулировать связанные параметры внутри объекта?

например, если параметры похожи


MyClass(String house, String street, String town, String postcode, String country, int foo, double bar) {
  super(String house, String street, String town, String postcode, String country);
  this.foo = foo;
  this.bar = bar;

тогда вы могли бы вместо этого иметь:


MyClass(Address homeAddress, int foo, double bar) {
  super(homeAddress);
  this.foo = foo;
  this.bar = bar;
}

То, что вы, вероятно, хотите сделать, это иметь класс Builder. Тогда вы бы сделали что-то вроде этого:

MyObject obj = new MyObjectBuilder().setXxx(myXxx)
                                    .setYyy(myYyy)
                                    .setZzz(myZzz)
                                    // ... etc.
                                    .build();

См. Стр. 8 и далее в этой презентации Джоша Блоха (PDF) или в этом обзоре Effective Java

Ну, использование шаблона построения может быть одним из решений.

Но как только вы придете к 20-30 параметрам, я предполагаю, что между параметрами существует высокая связь. Так что (как и предполагалось) оборачивать их в логически вменяемые объекты данных, вероятно, имеет смысл. Таким образом, объект данных уже может проверить правильность ограничений между параметрами.

Для всех моих проектов в прошлом, когда я пришел к выводу, что слишком много параметров (а это было 8, а не 28!), Я смог дезинфицировать код, создав лучшую модель данных.

Лучшее решение - не иметь слишком много параметров в конструкторе. Только параметры, действительно необходимые в конструкторе, являются параметрами, которые необходимы для правильной инициализации объекта. Вы можете иметь конструкторы с несколькими параметрами, а также иметь конструктор только с минимальными параметрами. Дополнительные конструкторы вызывают этот простой конструктор и после этого устанавливают другие параметры. Таким образом, вы можете избежать цепной проблемы с большим и большим количеством параметров, а также иметь несколько удобных конструкторов.

Поскольку вы ограничены Java 1.4, если вы хотите DI, Spring будет очень неплохим вариантом. DI полезен только в тех местах, где параметрами конструктора являются сервисы или что-то, что не изменяется во время выполнения.

Если у вас есть все эти разные конструкторы из-за того, что вам нужны переменные опции для построения объекта, вам следует серьезно подумать об использовании шаблона Builder.

Я действительно могу рекомендовать использовать Immutables или POJOBuilder при использовании шаблона компоновщика.

Рефакторинг для уменьшения количества параметров и глубины иерархии наследования - это почти все, что я могу придумать, потому что на самом деле ничто не поможет сохранить правильность более чем 20 параметров. Вы просто будете приходить на каждый звонок, просматривая документацию.

Одна вещь, которую вы могли бы сделать, это сгруппировать некоторые логически сгруппированные параметры в их собственный объект более высокого уровня, но у него есть свои проблемы.

Другие вопросы по тегам