Является ли метод Initialize запахом кода? - PullRequest
15 голосов
/ 01 ноября 2011

Я сейчас кодирую кучу систем.Они не являются производными от общего интерфейса.

Некоторые примеры систем: MusicSystem, PhysicsSystem, InputSystem и т. Д.

В настоящее время MusicSystem загружает много аудиофайлы в его конструкторе и, как следствие, при первом создании объекта может быть небольшая задержка.

Из-за этого, должен ли этот код, загружающий все аудиофайлы, помещаться вместо этого в метод Initialize()?Это позволяет программисту определить, когда он хочет загрузить аудиофайлы, но затем, если он забудет вызвать Initialize(), произойдет сбой программы.

Поскольку не всем системам необходим метод Initialize(), который должен искать программистчерез каждую систему, чтобы увидеть, есть ли в классе метод Initialize() и, если да, вызвать его.Это немного громоздко.

Какой подход предпочтительнее с точки зрения общих принципов проектирования?

Ответы [ 16 ]

21 голосов
/ 01 ноября 2011

Подумайте о других API, против которых вы написали код. Когда в последний раз API требовал, чтобы программист знал , чтобы вызвать метод init, в противном случае произошел сбой во время выполнения?

Как потребитель вашего API, я бы сошел с ума , если бы мне пришлось знать, как вызвать метод init после создания объекта. Я бы порекомендовал альтернативу, которую я видел и использовал из первых рук: документирование создания дорогостоящего объекта. Какой смысл откладывать дорогостоящую инициализацию, если требуется, чтобы программа не аварийно завершала работу?

12 голосов
/ 01 ноября 2011

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

Однако, альтернатива, которую вы можете попробовать, это lazy-loading .

Тогда вы можете взять свой торт и съесть его тоже:

  • клиентскому коду не нужно будет звонить Initialize
  • данные не будут загружены до тех пор, пока / или не понадобятся
11 голосов
/ 01 ноября 2011

Удаление тяжелой инициализации из конструктора не является запахом кода.

Тем не менее, полагаясь на то, что внешняя инициализация вызывает запах инициализации - он называется Temporal Coupling .

Создайте метод Initialize() в своем классе, но сделайте его private (или protected, если необходимо).

Также напишите метод EnsureInitialized(), который при необходимости запускает инициализацию,но только один раз за экземпляр.

Затем каждая открытая точка входа вашего класса должна вызывать EnsureInitialized() при запуске - ваша инициализация откладывается до точки первого использования.

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

5 голосов
/ 01 ноября 2011

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

Если вы убедитесь, что во всех ваших системах есть метод Initialize() (даже если он ничего не делает), тогда вашим пользователям не нужно беспокоиться о том, вызывать его или нет.

4 голосов
/ 01 ноября 2011

Здесь мало точек.

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

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

3 голосов
/ 01 ноября 2011

Просто идея из головы: добавить bool в конструктор - хочет ли инициатор инициализироваться в конструкторе.

2 голосов
/ 01 ноября 2011

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

2 голосов
/ 01 ноября 2011

Как насчет создания инициализированного частного / защищенного метода и вызова инициализации внутри, когда выполняется любой метод, требующий инициализации?

Например,

public class MyClass
{
  private bool _isInitialized;

  public MyClass()
  {
    ... only basic initializations...
  }

  private void initialize()
  {
    if (_isInitialized)
      return;

    // initialize here
  }

  public void SimpleMethod()
  {
    // doesn't need to initialize
  }

  public void ComplexMethod()
  {
    initialize();

    // do something...
  }
}
1 голос
/ 01 ноября 2011

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

Вы можете перенести свои длительные операции и операции ввода-вывода в специализированные оболочки, которые затем можно передать в виде параметров, таких как Stream, Connection, IDataReader и т. Д. В .Net. При этом будет более-менее предсказуемо, если операция может потреблять много ресурсов процессора, памяти или ввода-вывода.

1 голос
/ 01 ноября 2011

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

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

И тогда я подумал, а как насчет сценариев, в которых невозможно генерировать исключения? Настраивая все данные в конструкторе, невозможно указать на сбой, если нет возможности исключения, кроме средства доступа, такого как IsInitialised или чего-то еще. (Symbian является примером ОС без поддержки исключений.)

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

...