У меня есть ситуация, когда я хочу, чтобы свойство класса инициализировалось только в том случае, если к нему действительно осуществляется доступ, поскольку инициализация свойства включает в себя вызовы базы данных, которые могут быть ненужными. Я мог бы использовать метод получения, но это похоже на излишество, постоянно вызывая этот метод (я бы предпочел, чтобы свойство просто присутствовало, поскольку оно очень часто используется в 90% случаев).
Так что я думал об использовании PHP перегрузка и после небольшого теста, кажется, работает нормально.
class MyClass
{
public function getName()
{
return 'Bob';
}
public function doSomething()
{
var_dump($this->name);
var_dump($this->name);
var_dump($this->name);
}
public function __get($property)
{
var_dump('Getting property.');
if($property == 'name') {
$this->name = $this->getName();
return $this->name;
}
}
}
Вызов doSomething () делает именно то, что я хочу:
Строка (17) «Получение свойства.»
Строка (3) «Боб»
Строка (3) «Боб»
Строка (3) «Боб»
В первый раз свойство выбирается с помощью метода getName (), в последующие раз свойство теперь устанавливается напрямую и может быть доступно как обычно.
Так что это делает то, что я хочу. Вместо установки $ this-> name при инициализации класса он устанавливает его при первом обращении к нему. Я понимаю, что немедленным ответом, вероятно, будет то, что я могу просто вызвать getName () напрямую, но для этого конкретного случая я предпочел бы получить доступ через свойство. В настоящее время свойство постоянно устанавливается при инициализации класса, независимо от того, к нему обращаются или нет.
РЕДАКТИРОВАТЬ: Для пояснения, свойство на самом деле является объектом Eloquent в приложении реального мира, а не строка, содержащая имя! Выше был только пример, который я создал, чтобы проверить, что мое потенциальное решение будет работать правильно, и показать, что я хочу сделать. Кроме того, есть несколько из этих свойств. Я бы не стал инициализировать свойства в тех случаях, когда к ним нет доступа.
Мой вопрос. Является ли это добросовестным использованием Overloading, или другие разработчики его не одобряют? С точки зрения производительности, все ли хорошо, учитывая, что перегрузка используется только для первого доступа?
Спасибо
То, на что вы ссылаетесь, известно какленивая загрузка». В этом случае переменная-член будет установлена только в случае ее вызова. При первом вызове значение массируется, затем сохраняется, поэтому последующие вызовы не страдают от производительности, связанной с необходимостью повторного массажа данных.
но для этого конкретного случая я бы предпочел доступ через собственность
А? Так что вы бы лучше $object->someMemberVariable
против $object->getValueForMember()
? Зачем? Последний предоставляет вам возможность изменять данные (при необходимости) в одном месте, по сравнению с необходимостью возложить это бремя на вызывающий код, что может привести к повторной логике, чего вы не хотите. Подумайте о переменной метки времени, а не форматируйте дату везде, где вы звоните $object->createdAt
Вы можете отформатировать дату один раз, а затем сделать ее доступной везде, где это необходимо.
В своем фрагменте кода вы уже перегружаете __get
позвоните б / к, вы рассматриваете это как обертку для конкретного аксессуара.
Лучшее использование __get
или же __set
вызовы находятся в ORM, где они не создают потенциально сотни конкретных методов доступа, а действуют так, как если бы они существовали через __call
метод.
— Обновить —
Вы в значительной степени ответили на свой вопрос. Что вы делаете здесь микро-оптимизации, который имеет убывающую отдачу. Но если вы сделаете, как вы заявили
сначала другие области оптимизации (производительность базы данных, чтение файлов,
хранение вещей в памяти и т. д.))
Вы получите больше рентабельности.
Вы можете сделать то, что Алекс предложил в комментариях, вы можете добавить метод-обертку к объекту пользователя:
$user->getAddress()->getStreetName();
// Becomes
$user->getStreetName();
Хотя вначале это может выглядеть оптимизированным, он делает то же самое, просто немного изменяя вызывающий код для минимального усиления. ТЕМ НЕ МЕНИЕ Есть моменты, когда добавление, которое я называю «удобными обертками», может принести некоторую пользу. Удобная оболочка просто упаковывает вызовы, чтобы облегчить сложность вызывающего кода:
echo sprintf('%s %s %s', $user->getFirstName(), $user->getMiddleName(), $user->getLastName());
// Becomes
echo $user->getFullName();
Еще один ТЕМ НЕ МЕНИЕ, некоторые ORM (и фреймворки) будут маскировать тот факт, что они будут выполнять дополнительные вызовы базы данных для извлечения данных (отложенная загрузка). Таким образом, используя приведенный выше пример адреса; даже если вы сделали вызов базы данных, чтобы вернуть данные в user
стол, вызов getAddress()
потребует еще один вызов базы данных, чтобы вернуть данные в user_address
Таблица. Итак, что вы можете сделать в этом случае, это реализовать то, что я называю супер объект, очень похоже на обычный объект, но только с плащом, JK. Вот что я имею в виду:
// No extra DB call, as id is part of the primary table and was pulled on initial DB call
$user->getId();
// Even tho the ORM knows there is a relationship here it wasn't smart enough to pull it in on the initial call, so another DB call must be made
$user->getAddress()->getStreetName();
// What we want to do is incorporate our own method to retrieve a super object, so we can join all tables during initial db call and make the data available to the view
$query = 'SELECT u.*, ua.* FROM user AS a LEFT OUTER JOIN user_address AS ua WHERE u.id = 1';
// Hydrate user object accordingly
....
return $hydratedObject;
Теперь, с суперобъектом, любые обращения к вспомогательным данным, хранящимся в других таблицах, не будут стоить вам попадания в БД, что действительно позволит оптимизировать ваше приложение.
Вид там пошёл по касательной, но короче говоря, нет ничего плохого в таких звонках $user->getAddress()->getStreetName()
, На самом деле это знать как fluent interface
и может получить довольно долго в зависимости от того, как были написаны классы:
$user->getCats()->getFirst()->getColor();
Других решений пока нет …