Керування конструкторами з багатьма параметрами на 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.