Что-то не так с немедленными действиями в конструкторах? - PullRequest
15 голосов
/ 20 мая 2010

У меня есть такие классы:

class SomeObject
{
    public function __construct($param1, $param2)
    {
       $this->process($param1, $param2);
    }
    ...
}

Так что я могу мгновенно «вызвать» ее как какую-то глобальную функцию, такую ​​же как

new SomeObject($arg1, $arg2);

, который имеет преимущества

  • оставаясь кратким,
  • легко понять,

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

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

Пояснение:

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

Пример:

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

new Email('to@example.com', 'Subject line', 'Body Text');

Конечно, я избегаю его чрезмерного использования, но, на мой взгляд, это действительно удобно.

Ответы [ 8 ]

8 голосов
/ 20 мая 2010

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

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

Сохранить конструктор для строительства.

5 голосов
/ 20 мая 2010

Это плохая практика.

Использование конструктора в качестве глобальной функции совсем не то, для чего она предназначена, и ее легко спутать. Это совсем не легко понять.

Если вам нужна глобальная функция, объявите ее:

function myGlobalFunction(){ return $something; }

Это действительно не так сложно ...

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

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

Если вам нужно всегда предпринимать определенные действия после создания нового экземпляра класса, попробуйте фабрику:

 class myFactory{
       public static function makeObject(){
           $obj = new Object();
           $obj->init1();
           return $obj;
       }
 }

 $obj = myFactory::makeObject();
4 голосов
/ 20 мая 2010

Бритва Оккама говорит: entia non sunt multiplicanda praeter needitatem , буквально: «сущности не должны умножаться сверх необходимости». Нет ничего плохого в том, чтобы выполнять работу в конструкторе [*], но не требуйте, чтобы вызывающие лица создавали объект, который они не используют, просто чтобы получить побочные эффекты его конструктора.

[*] Предполагая, что вы знакомы с механизмом, который использует ваш язык программирования, чтобы указать на сбой конструктора. Возвращение кода успеха / неудачи из конструктора может не быть вариантом, поэтому, если вы хотите избежать исключения в случае сбоя, вы можете ограничиться установкой флагов «можно использовать» в объекте, что не очень хорошо. для удобства использования либо.

1 голос
/ 20 мая 2010

Вещи, которые следует учитывать относительно этого шаблона:

  • Принцип нарушения единой ответственности
  • Реализация функционального разложения Антипаттерн
  • Несоблюдение общих ожиданий потребителей

Принцип единой ответственности

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

Функциональная декомпозиция Антипаттерна

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

Общие ожидания потребителей

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

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

Допустимое использование?

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

/* This is a common usage of this class */
File f;
f.Open(path);

/* This constructor constructs the object and puts it in the open state in one step 
 * for convenience */
File f(path);

Но даже это сомнительно. Сохраняет ли класс файла путь к файлу внутри или он просто используется для открытия дескриптора файла? Если он хранит путь к файлу, то теперь Open () и новый конструктор несут более чем одну ответственность (SetPath (p) и Open ()). В этом случае, возможно, удобный конструктор File (path) не должен открывать файл, а должен просто указать путь в объекте.

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

1 голос
/ 20 мая 2010

Я думаю, что это плохая практика по двум причинам.

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

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

Что касается второго пункта ... если конструктор содержит операцию, которая непредсказуемо генерирует исключения (в отличие от исключений, вызванных, например, из-за проверки параметров), тогда ваш код обработки исключений становится неловким. Рассмотрим следующий пример в C #. Обратите внимание, что хороший дизайн выглядит более элегантно. Не имеет значения тот факт, что четко названный метод, по крайней мере, на порядок более читабелен.

public class BadDesign : IDisposable
{
  public BadDesign()
  {
    PerformIOBoundOperation();
  }

  private void PerformIOBoundOperation() { }
}

public class GoodDesign : IDisposable
{
  public GoodDesign()
  {

  }

  public void PerformIOBoundOperation() { }
}

public static void Main()
{
  BadDesign bad = null;
  try
  {
    bad = new BadDesign();
  }
  catch
  {
    // 'bad' is left as null reference. There is nothing more we can do.
  }
  finally
  {
    if (bad != null)
    {
      bad.Dispose();
    }
  }

  GoodDesign good = new GoodDesign();
  try
  {
    good.PerformIOBoundOperation();
  }
  catch
  {
    // Do something to 'good' to recover from the error.
  }
  finally
  {
    good.Dispose();
  }
}
0 голосов
/ 20 мая 2010

Ваш класс технически в порядке, и он может просто сводиться к личной философии.

но для других, читающих это, которые могут получить некоторые безумные идеи о конструкторах, прочитайте это:

http://www.ibm.com/developerworks/java/library/j-jtp0618.html

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

0 голосов
/ 20 мая 2010

Это лаконично, но не является идиоматически удаленным, поэтому другим нелегко понять.

В будущем вам, возможно, будет нелегко понять это.

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

0 голосов
/ 20 мая 2010

На мой взгляд, это зависит от цели функции.

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

В общем смысле, я полагаю, что (общедоступные) функции, которые вы хотите вызывать во время конструктора, обычно являются идемпотентными функциями, которые манипулируют локальным состоянием, поэтому такие вещи, как заполнение кэша / предварительная выборка / т. Д.

Если у вас есть объект, который выполняет какую-то обработку ввода, я бы определенно избегал

s = new MyObject(a, b)
return s.getResult()

в пользу

s = new MyObject() // perhaps this is done elsewhere, even once at startup
return s.process(a, b)

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

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

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