Сколько работы нужно выполнить в конструкторе? - PullRequest
50 голосов
/ 16 ноября 2008

Должны ли операции, которые могут занять некоторое время, выполняться в конструкторе, или если объект будет сконструирован, а затем инициализирован позже.

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

Какое элегантное решение для этого?

Ответы [ 18 ]

48 голосов
/ 16 ноября 2008

Подведем итог:

  • Как минимум, ваш конструктор должен настроить объект так, чтобы его инварианты были истинными.

  • Ваш выбор инвариантов может повлиять на ваших клиентов. (Обещает ли объект быть готовым к доступу в любое время? Или только в определенных состояниях?) Конструктор, который заботится обо всех настройках -фронт может сделать жизнь клиентов класса проще.

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

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

  • Задержка вычисления до тех пор, пока конструктор не станет эффективной оптимизацией; может оказаться ненужным выполнять всю работу. Это зависит от применения и не должно определяться преждевременно.

  • В целом, это зависит.

24 голосов
/ 16 ноября 2008

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

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

13 голосов
/ 16 ноября 2008

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

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

10 голосов
/ 16 ноября 2008

Зависит (типичный ответ CS). Если вы создаете объекты при запуске для долго работающей программы, то нет проблем с выполнением большой работы в конструкторах. Если это часть графического интерфейса, где ожидается быстрый ответ, это может быть неуместно. Как всегда, лучший ответ - сначала попробовать самый простой способ, профилировать и оптимизировать оттуда.

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

7 голосов
/ 16 ноября 2008

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

4 голосов
/ 16 ноября 2008

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

3 голосов
/ 05 января 2010

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

Например, допустим, мне нужно отобразить следующий класс компании в подробном представлении:

public class Company
{
    public int Company_ID { get; set; }
    public string CompanyName { get; set; }
    public Address MailingAddress { get; set; }
    public Phones CompanyPhones { get; set; }
    public Contact ContactPerson { get; set; }
}

Поскольку я хочу отображать всю информацию о компании, которую я имею, в подробном представлении, мой конструктор будет содержать весь код, необходимый для заполнения каждого свойства. Учитывая, что это сложный тип, конструктор Company будет запускать выполнение конструктора Address, Phones и Contact.

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

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

3 голосов
/ 16 ноября 2008

Я бы согласился, что долго работающие конструкторы не являются плохими по своей сути. Но я бы сказал, что ты почти всегда ошибаешься. Мой совет похож на совет Хьюго, Рича и Литба:

  1. свести к минимуму работу, которую вы выполняете в конструкторах, - сфокусировать внимание на состоянии инициализации.
  2. Не бросайте из конструкторов, если вы не можете избежать этого. Я пытаюсь только бросить std :: bad_alloc.
  3. Не вызывайте API-интерфейсы ОС или библиотек, если вы не знаете, что они делают - большинство из них могут блокировать. Они будут работать быстро на вашем устройстве разработки и тестовых машинах, но в полевых условиях они могут быть заблокированы на длительные периоды времени, так как система занята чем-то другим.
  4. Никогда, никогда не делаю ввод / вывод в конструкторе - любого рода. Обычно на ввод / вывод влияют очень длинные задержки (от сотен миллисекунд до секунд). I / O включает
    • Дисковый ввод / вывод
    • Все, что использует сеть (даже косвенно). Помните, что большинство ресурсов могут быть автономными.

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

Конечно, зло долго работающего конструктора зависит от двух вещей:

  1. Что означает «длинный»
  2. Как часто в данный период строятся объекты с «длинными» конструкторами.

Теперь, если 'long' - это просто несколько 100 дополнительных тактов работы, то это не очень долго. Но конструктор входит в сотню микросекундного диапазона, я полагаю, он довольно длинный. Конечно, если вы создаете только один из них или создаете их редко (скажем, каждые несколько секунд), вы вряд ли увидите проблемы из-за длительности в этом диапазоне.

Частота - важный фактор, 500-центовый процессор не является проблемой, если вы строите только несколько из них: но создание миллиона из них создаст существенную проблему производительности.

Давайте поговорим о вашем примере: заполнение дерева объектов каталога внутри объекта "Class Directory". (обратите внимание, я собираюсь предположить, что это программа с графическим интерфейсом). Здесь ваша длительность CTOR не зависит от кода, который вы пишете - она ​​зависит от времени, которое требуется для перечисления произвольно большого дерева каталогов. Это достаточно плохо на локальном жестком диске. Еще более проблематично при удаленном (сетевом) ресурсе.

А теперь представьте, что делаете это в своем потоке пользовательского интерфейса - ваш пользовательский интерфейс остановится на несколько секунд, 10 секунд или даже потенциальных минут. В Windows мы называем это зависанием пользовательского интерфейса. Они плохо плохо плохо (да мы их ... да мы упорно работаем, чтобы устранить их).

UI Зависания - это то, что может заставить людей действительно ненавидеть ваше программное обеспечение.

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

2 голосов
/ 16 ноября 2008

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

В вашем объекте структуры каталогов: я недавно внедрил браузер samba (windows share) для своего HTPC, и, поскольку он был невероятно медленным, я решил фактически инициализировать каталог только при касании. например Сначала дерево будет состоять только из списка машин, затем всякий раз, когда вы просматриваете каталог, система автоматически инициализирует дерево с этого компьютера и выводит каталог на один уровень глубже и т. Д.

В идеале, я думаю, что вы могли бы даже пойти на написание рабочего потока, который сканирует каталоги в первую очередь и отдает приоритет каталогу, который вы просматриваете в данный момент, но, как правило, это слишком большая работа для чего-то простого; )

1 голос
/ 16 ноября 2008

столько, сколько нужно и не больше.

Конструктор должен перевести объект в работоспособное состояние, следовательно, как минимум, ваши переменные класса должны быть инициированы. То, что инициированные средства могут иметь широкое толкование. Вот надуманный пример. Представьте, что у вас есть класс, который отвечает за предоставление N! на ваше приложение для звонков.

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

Другой способ реализовать это - иметь переменную класса, которая является массивом. Конструктор установит все значения в -1, чтобы указать, что значение еще не было вычислено. функция-член будет выполнять ленивую оценку. Он смотрит на элемент массива. Если это -1, он вычисляет его, сохраняет и возвращает значение, в противном случае он просто возвращает значение из массива.

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

Еще один способ реализовать это - сохранить значения в текстовом файле и использовать N в качестве основы для смещения в файле, из которого нужно извлечь значение. В этом случае конструктор откроет файл, а деструктор закроет файл, в то время как метод сделает что-то вроде fseek / fread и вернет значение.

Другим способом его реализации будет предварительное вычисление значений и сохранение их в виде статического массива, на который может ссылаться класс. У конструктора не будет работы, и метод достигнет массива, чтобы получить значение и вернуть его. Несколько экземпляров будут использовать этот массив.

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

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