Керування конструкторами з багатьма параметрами на Java


105

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

Я визнаю, що використання автоматичного DI через щось на зразок Guice було б непогано, але через деякі технічні причини ці конкретні проекти обмежуються Java.

Конвенція про упорядкування аргументів в алфавітному порядку за типом не працює, тому що якщо тип буде відновлено заново (Коло, яке ви передавали для аргументу 2, тепер є формою), воно може раптом вийти з ладу.

Це питання може бути конкретним і загрожує критикою "Якщо це ваша проблема, ви робите це неправильно на рівні дизайну", але я просто шукаю будь-які точки зору.

Відповіді:


264

Модель дизайнера 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 винести виняток. І це дозволяє нам мати аргументи за замовчуванням / за бажанням без необхідності відстежувати будь-який порядок аргументів, оскільки будь-який порядок цих викликів буде працювати однаково добре.


10
Звичайно, при статичному імпорті вам взагалі ніколи навіть не доводиться "бачити" цих "будівельників". Наприклад, у вас може бути ім'я статичних методів (ім'я рядка), яке повертає будівельника і студент (StudentBuilder), який повертає студента. Звідси Студент (ім'я ("Джо"). Вік (15) .мото ("Я намочив себе"));
— oxbow_lakes

2
@oxbow_lakes: У вашому прикладі, який клас має назву статичного методу (ім'я рядка)?
— user443854

Технічно кажучи, можна використовувати клас Student для створення нового студента. Я додав методи всередині студентського класу, і це спрацювало чудово. Таким чином мені не потрібно було мати іншого класу будівельників. Я не впевнений, чи бажано це. Чи є причина використовувати інший (StudentBuilder) клас для його побудови?
— WVrock

1
@WVrock: Це залежить від вашої реалізації. Як я кажу у своїй відповіді, виконання цього курсу з самим класом студента може потенційно залишити клас у напівініціалізованому стані, наприклад, якщо у вас є обов'язкове поле, яке ще не було ініціалізовано.
— Eli Courtwright

@EliCourtwright Я думаю, мова йде про перевагу / дизайн коду. Замість того, щоб змусити конструктор кинути виняток, я зробив buildStudent()метод кинути виняток.
— WVrock

24

Чи можете ви інкапсулювати пов'язані параметри всередині об'єкта?

наприклад, якщо параметри схожі


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



8

Що ж, використання шаблону будівельника може бути одним із рішень.

Але як тільки ви підійдете до 20 - 30 параметрів, я б припустив, що між параметрами існує висока залежність. Отже, (як це пропонується), вбудовувати їх у логічно здорові об'єкти даних, мабуть, має найбільш сенс. Таким чином об'єкт даних вже може перевірити обґрунтованість обмежень між параметрами.

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


4

Оскільки ви обмежені Java 1.4, якщо ви хочете, щоб DI, то весна була б дуже пристойним варіантом. DI корисний лише в місцях, де параметри конструктора - це послуги або щось, що не змінюється під час виконання.

Якщо у вас є всі ці різні конструктори через те, що ви хочете змінних варіантів того, як побудувати об’єкт, вам слід серйозно розглянути можливість використання шаблону Builder.


Параметри - це в основному сервіси, як ви згадали, і тому DI - це те, що мені потрібно. Я думаю, що модель «Будівельник», згадана в декількох інших відповідях, - це саме те, на що я сподівався.
— Стів Армстронг

4

Найкраще рішення - не мати занадто багато параметрів у конструкторі. Тільки параметри, дійсно потрібні конструктору, - це парами, які потрібно правильно ініціалізувати об'єкт. Ви можете мати конструктори з декількома параметрами, але також мати конструктор лише з мінімальними параметрами. Додаткові конструктори називають цей простий конструктор і після цього сетером встановлюють інші параметри. Таким чином ви зможете уникнути проблеми з ланцюгом все більше і більше парам, а також мати деякі конструктори зручності.



1

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

Одне, що можна зробити, - це згрупувати деякі логічно згруповані параметри у власний об'єкт вищого рівня, але у цього є свої проблеми.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.