Какие вещи лучше не делать в конструкторе? - PullRequest
7 голосов
/ 08 февраля 2009

Я начал с составления вопроса: «Как лучше всего выполнить модульное тестирование на конструкторе (например, __construct () в PHP5)», но при чтении связанных вопросов я увидел несколько комментариев, предположить, что установка переменных-членов или выполнение каких-либо сложных операций в конструкторе не допускаются.

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

Я думал, что преимущества такого подхода были:

1) этот код клиента всегда будет наверняка иметь значение для этого переменная-член всякий раз, когда объект этого класса создается, и

2) сохраняет шаг в коде клиента (один из которых может быть пропущено), например,

$Thing = new Thing;
$Thing->initialize($var);

когда мы могли бы просто сделать это

$Thing = new Thing($var);

и покончим с этим.

Это нет-нет? Если так, то почему?

Ответы [ 10 ]

16 голосов
/ 08 февраля 2009

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

Мой список дел и пожертвований:

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

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

8 голосов
/ 08 февраля 2009

Это часто встречается в дискуссиях на C ++, и общий вывод, к которому я пришел, был такой:

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

  • (x, y) координата (или любая другая структура, которая является просто прославленным кортежем)
  • Таблица поиска сокращений штатов США

Если объект получает ресурсы, которыми он может управлять, он может быть выделен в конструкторе:

  • дескриптор открытого файла
  • выделенная память
  • дескриптор / указатель во внешнюю библиотеку

Если объект получает ресурсы, которыми он не может полностью управлять, он должен быть выделен вне конструктора:

  • TCP соединение
  • соединение с БД
  • слабая ссылка

Всегда есть исключения, но это касается большинства случаев.

6 голосов
/ 08 февраля 2009

Конструкторы предназначены для инициализации объекта, поэтому

$Thing = new Thing($var);

вполне приемлемо.

4 голосов
/ 08 февраля 2009

Задача конструктора - установить инварианты экземпляра .

Все, что не способствует этому, лучше хранить в конструкторе.

2 голосов
/ 08 февраля 2009

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

1 голос
/ 08 февраля 2009

Вы определенно не должны заставлять клиента звонить

$thing->initialize($var)

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

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

Извиняюсь за включение синтаксиса Java в ответ Python, но:

// Constructor
public MyObject(MyType initVar) {
      this.initVar = initVar;
}

private void lazyInitialize() {
    if(initialized) {
        return
    }
    // initialization code goes here, uses initVar
}

public SomeType doSomething(SomeOtherType x) {
    lazyInitialize();
    // doing something code goes here
}

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

0 голосов
/ 23 февраля 2009

Если $ var абсолютно необходим для работы $ Thing, то это DO

0 голосов
/ 08 февраля 2009

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

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

0 голосов
/ 08 февраля 2009

Зависит от того, какой тип системы вы пытаетесь спроектировать, но в целом я считаю, что конструкторы лучше всего использовать только для инициализации «состояния» объекта, но не для выполнения каких-либо переходов состояния сами. Лучше всего просто установить его по умолчанию.

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

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

__ конструкт ($ аргументы)
ручка ()
render (исключение $ ex = null)

0 голосов
/ 08 февраля 2009

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

Объяснить.

Если бы у меня был класс базы данных. Где конструктор это соединение с базой данных Так

$db = new dbclass;

А теперь я подключен к базе данных.

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

class users extends dbclass
{
    // some methods
}

$users = new users 
// by doing this, we have called the dbclass's constructor again
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...