Сколько аргументов конструктора слишком много?

Допустим, у вас есть класс Customer, который содержит следующие поля:

  • UserName
  • Email
  • Имя
  • Фамилия

Скажем также, что в соответствии с вашей бизнес-логикой все объекты Customer должны иметь эти четыре свойства.

Теперь мы можем сделать это довольно легко, заставив конструктор указать каждое из этих свойств. Но довольно легко увидеть, как это может выйти из-под контроля, когда вы вынуждены добавить дополнительные обязательные поля в объект Customer.

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

Есть ли альтернативы этому или вам просто нужно решить, слишком ли много X аргументов конструктора для вас, чтобы жить с ним?

113 голосов | спросил Kevin Pang 2 ndEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 02 Sep 2008 22:44:31 +0400 2008, 22:44:31

13 ответов


0

Два подхода к проектированию для рассмотрения

Шаблон sence

Шаблон плавный интерфейс

Они оба похожи по своему намерению: мы медленно создаем промежуточный объект, а затем создаем наш целевой объект за один шаг.

Пример свободного интерфейса в действии:

public class CustomerBuilder {
    String surname;
    String firstName;
    String ssn;
    public static CustomerBuilder customer() {
        return new CustomerBuilder();
    }
    public CustomerBuilder withSurname(String surname) {
        this.surname = surname; 
        return this; 
    }
    public CustomerBuilder withFirstName(String firstName) {
        this.firstName = firstName;
        return this; 
    }
    public CustomerBuilder withSsn(String ssn) {
        this.ssn = ssn; 
        return this; 
    }
    // client doesn't get to instantiate Customer directly
    public Customer build() {
        return new Customer(this);            
    }
}

public class Customer {
    private final String firstName;
    private final String surname;
    private final String ssn;

    Customer(CustomerBuilder builder) {
        if (builder.firstName == null) throw new NullPointerException("firstName");
        if (builder.surname == null) throw new NullPointerException("surname");
        if (builder.ssn == null) throw new NullPointerException("ssn");
        this.firstName = builder.firstName;
        this.surname = builder.surname;
        this.ssn = builder.ssn;
    }

    public String getFirstName() { return firstName;  }
    public String getSurname() { return surname; }
    public String getSsn() { return ssn; }    
}


import static com.acme.CustomerBuilder.customer;

public class Client {
    public void doSomething() {
        Customer customer = customer()
            .withSurname("Smith")
            .withFirstName("Fred")
            .withSsn("123XS1")
            .build();
    }
}
ответил toolkit 2 ndEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 02 Sep 2008 23:08:20 +0400 2008, 23:08:20
0

Я вижу, что некоторые люди рекомендуют семь в качестве верхнего предела. Очевидно, это не правда, что люди могут держать в голове семь вещей одновременно; они могут вспомнить только четыре (Сьюзен Вайншенк, 100 вещей, которые каждый дизайнер должен знать о людях , 48). Несмотря на это, я считаю, что четыре - это что-то вроде высокой земной орбиты. Но это потому, что мое мышление было изменено Бобом Мартином.

В Чистый код дядя Боб утверждает три для общего верхнего предела числа параметров. Он делает радикальное утверждение (40):

  

Идеальное число аргументов для функции - ноль (нильадик). Далее следует одно (монадическое), за которым следует два (диадическое). По возможности следует избегать трех аргументов (триадных). Более трех (полиадические) требуют особого обоснования, и их не следует использовать в любом случае.

Он говорит это из-за читабельности; но также из-за тестируемости:

  

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

Я призываю вас найти копию его книги и прочитать его полное обсуждение аргументов функций (40-43).

Я согласен с теми, кто упомянул принцип единой ответственности. Мне трудно поверить, что класс, которому нужно более двух или трех значений /объектов без разумных значений по умолчанию, действительно несет только одну ответственность и не будет лучше, если извлечен другой класс.

Теперь, если вы вводите свои зависимости через конструктор, аргументы Боба Мартина о том, как легко вызывать конструктор, не очень применимы (потому что обычно в вашем приложении есть только одна точка, где вы подключаете это, или у вас даже есть структура, которая делает это за вас). Тем не менее, принцип единой ответственности по-прежнему актуален: если у класса четыре зависимости, я считаю, что он пахнет большой работой.

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

ответил Keith Pinson 20 MarpmWed, 20 Mar 2013 22:17:12 +04002013-03-20T22:17:12+04:0010 2013, 22:17:12
0

Я думаю, что ваш вопрос больше касается дизайна ваших классов, чем количества аргументов в конструкторе. Если бы мне понадобилось 20 частей данных (аргументов) для успешной инициализации объекта, я бы, вероятно, подумал о том, чтобы разбить класс.

ответил vitule 2 ndEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 02 Sep 2008 22:55:35 +0400 2008, 22:55:35
0

Стив Макконнелл пишет в Code Complete, что у людей возникают проблемы с тем, чтобы держать в голове больше 7 вещей за раз, так что это будет число, под которым я пытаюсь остаться.

ответил Booji Boy 2 ndEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 02 Sep 2008 22:52:00 +0400 2008, 22:52:00
0

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

В C # я понимаю, что такое руководство по проектированию, что это не единственный способ справиться с ситуацией. В частности, с объектами WPF вы обнаружите, что классы .NET предпочитают конструкторы без параметров и будут генерировать исключения, если данные не были инициализированы в желаемое состояние перед вызовом метода. Это, вероятно, в основном характерно для компонентно-ориентированного проектирования; Я не могу придумать конкретный пример класса .NET, который ведет себя таким образом. В вашем случае это определенно увеличит нагрузку на тестирование, чтобы гарантировать, что класс никогда не будет сохранен в хранилище данных, если свойства не были проверены. Честно говоря, из-за этого я бы предпочел подход «конструктор устанавливает требуемые свойства», если ваш API установлен в камне или не публично.

В чем я уверен , так это в том, что, вероятно, существует множество методологий, которые могут решить эту проблему, и каждая из них представляет свой собственный набор проблем. Лучше всего выучить как можно больше шаблонов и выбрать лучший для работы. (Разве это не отговорка от ответа?)

ответил OwenP 2 ndEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 02 Sep 2008 22:55:05 +0400 2008, 22:55:05
0

Если у вас есть неимоверно много аргументов, просто объедините их в классы Struct /POD, предпочтительно объявленные как внутренние классы создаваемого вами класса. Таким образом, вы все равно можете требовать поля, делая код, который вызывает конструктор, достаточно читабельным.

ответил McKenzieG1 2 ndEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 02 Sep 2008 22:48:09 +0400 2008, 22:48:09
0

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

Затем создайте функции getter и setter для каждого свойства, чтобы можно было изменить значения по умолчанию.

Java-реализация:

public static void setEmail(String newEmail){
    this.email = newEmail;
}

public static String getEmail(){
    return this.email;
}

Это также хорошая практика для обеспечения безопасности ваших глобальных переменных.

ответил helloandre 2 ndEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 02 Sep 2008 22:51:22 +0400 2008, 22:51:22
0

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

ответил Ethan Gunderson 2 ndEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 02 Sep 2008 22:50:50 +0400 2008, 22:50:50
0

Стиль очень важен, и мне кажется, что если есть конструктор с более чем 20 аргументами, то дизайн должен быть изменен. Предоставьте разумные значения по умолчанию.

ответил Adam Hollidge 2 ndEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 02 Sep 2008 22:50:45 +0400 2008, 22:50:45
0

Я согласен с ограничением в 7 предметов, которое упоминает Бужибой. Кроме того, возможно, стоит взглянуть на анонимные (или специализированные) типы, IDictionary или косвенное обращение через первичный ключ к другому источнику данных.

ответил spoulson 2 ndEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 02 Sep 2008 22:54:49 +0400 2008, 22:54:49
0

Я бы инкапсулировал подобные поля в собственный объект со своей собственной логикой построения /проверки.

Скажем, например, если у вас есть

  • BusinessPhone
  • BusinessAddress
  • HomePhone
  • HomeAddress

Я бы создал класс, который хранит телефон и адрес вместе с тегом, указывающим, является ли он «домашним» или «служебным» телефоном /адресом. А затем уменьшите 4 поля до простого массива.

ContactInfo cinfos = new ContactInfo[] {
    new ContactInfo("home", "+123456789", "123 ABC Avenue"),
    new ContactInfo("biz", "+987654321", "789 ZYX Avenue")
};

Customer c = new Customer("john", "doe", cinfos);

Это должно сделать его менее похожим на спагетти.

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

И следующие также возможные решения:

  • Распространяйте логику проверки вместо того, чтобы хранить ее в одном классе. Проверять, когда пользователь вводит их, а затем снова проверять на уровне базы данных и т. Д.
  • Создайте класс CustomerFactory, который поможет мне создать Customer s
  • @ Марсио тоже интересно ...
ответил chakrit 2 ndEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 02 Sep 2008 23:31:20 +0400 2008, 23:31:20
0

Просто используйте аргументы по умолчанию. На языке, который поддерживает аргументы метода по умолчанию (например, PHP), вы можете сделать это в сигнатуре метода:

public function doSomethingWith($this = val1, $this = val2, $this = val3)

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

Конечно, вы также можете установить значения по умолчанию при объявлении полей, если сочтете это целесообразным.

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

ответил Brian Warshaw 2 ndEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 02 Sep 2008 22:49:29 +0400 2008, 22:49:29
0

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

ответил Aldie 2 ndEurope/Moscowp30Europe/Moscow09bEurope/MoscowTue, 02 Sep 2008 22:47:40 +0400 2008, 22:47:40

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132