Рекомендации OOP (в частности, PHP)

Я развиваюсь в PHP некоторое время, но только недавно перешел на подход ООП.

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

Например, скажем, у меня есть 2 объекта, User и Listing

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

Насколько я могу видеть (просьба сообщить, если нет) У меня есть 3 варианта, как это сделать.

  • Создайте новый пользовательский объект и получите доступ к ресурсу с достаточным доступом через $user → theProperty

  • Сделать требуемое свойство локальным свойством листинга и заполнить его, когда листинг инициализируется (например, через соединение sql)

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

Мне кажется, что опции 1 и 2 более строго подчиняются правилам ООП, но имеют производительность, из-за инициализации всего объекта просто для извлечения 1 свойства. Вариант 3 был бы наименее интенсивным с памятью, но обойдется ООП в целом.

Кроме того, с точки зрения заполнения объектов при создании большинство моих объектов получают большинство своих свойств, заполненных одним методом "fill", вскоре после инициализации (отсюда требуется только 1 запрос БД). Будет ли это вообще рассматриваться как лучшая практика или было бы целесообразнее использовать отдельные методы для получения этих свойств, заполняя их и когда они нужны?

Я понимаю, что на это могут не быть "правильные" ответы, но может ли кто-нибудь дать совет о лучших способах подхода к этой ситуации?

Большое спасибо Ник

Ответы

Ответ 1

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

Соблюдайте следующие правила:

  • Загружает объект без его связей сразу.
  • Если вам нужен связанный объект, загрузите его в то время, когда оно вам нужно, в соответствии с правилом 1

Многие ORM работают таким образом, например Doctrine (http://www.doctrine-project.org). Он назывался ленивой загрузкой.

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

Ответ 2

Вариант 1 является предпочтительным способом ООП.

Кроме того, в php вы можете написать объект-оболочку, который содержит только user_id и необходимые вам свойства.

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

Это может быть достигнуто с помощью методов __get(), __set() и __call().

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

Код должен выглядеть примерно так:

class LazyObject {

  private 
    $propertySubset = array(),
    $realObject;


 function __call($method, $args) {
    if ($this->realObject === null) {
      $this->initRealObject();
    }
    return call_user_func_array(array($this->realObject, $method), $args);
  }

  // __get, __set and initRealObject implementation goes here

}

Предостережения

  • Вы можете использовать ссылки: myMethod(&$ref) и $shortcut = &$object->prop wont work
  • Вам нужно вручную проверить, заполнили ли вы оболочку достаточными свойствами. Недостаточно свойств приведет к распределению запросов, и обертка только замедляется.
  • Использование интерфейсов (например, ArrayAccess) не будет работать, если вы не реализуете их в lazyObject

PS.
Это следует рассматривать как взлом оптимизации. Поэтому реализуйте это только в том случае, если производительность становится проблемой;

Ответ 3

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

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

Вы говорите, Ник, нет "правильного" ответа. Но мы можем сказать следующее:

В идеальной практике OOP идеальный объект перечисления должен иметь свойство $listing- > user, которое представляет собой объект, представляющий Листинг. Для повышения производительности этот объект пользователя не должен создаваться, как только создается экземпляр $. В традиционном кодировании Java у вас будет метод → getUser(). В PHP вы можете создать этот объект, как только вы вызываете свойство → user.

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

Если это так, то вы должны немного отойти от идеальной объектной модели, чтобы быть ближе к вашему бизнес-процессу. Затем вы получаете только требуемое свойство пользователя, которое может быть хорошим компромиссом. Например: $listing- > getUserProp1(). Если вам понадобятся другие пользовательские свойства в будущем, вы можете изменить метод на $listing- > getUserProprty ('prop1').

Но этот параметр хорош только в том случае, если вам иногда требуется свойство User. Если на самом деле вам это нужно, каждый раз, когда у вас есть объект листинга, тогда у вас уже есть свойство User, уже инициализированное другими свойствами листинга. Это вариант №2. Кажется, это очень хорошее решение, в котором вам нужен поток данных, который минимизирован, и если вам не нужна другая информация пользователя рядом с объектом Листинг.

Ответ 4

Я считаю, что вам нужно взглянуть на шаблон проектирования Data Transfer Objects:

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

При некоторой параметризации ваш бизнес-уровень может возвращать DTO, которые имеют определенный набор из одного или нескольких объектов, возвращаемых уровнем доступа к данным.

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

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