我有一种情况,我希望仅在实际访问时初始化类属性,因为初始化该属性包括可能不必要的数据库调用。我可以使用 getter 方法,但这似乎是矫枉过正,不断调用此方法(我希望该属性只是在那里,因为它在 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 对象,而不是包含名称的字符串!以上只是我创建的示例,用于测试我的潜在解决方案是否可以正常工作,并显示我想要做什么。此外,还有一些这样的属性。我宁愿在无法访问属性的情况下不初始化属性。
我的问题。这是对重载的合理使用,还是其他开发人员通常会皱眉?性能方面,考虑到过载仅用于首次访问,一切都很好吗?
谢谢
您所指的称为"延迟加载"。因为成员变量只有在被调用时才会被设置。在第一次调用时,该值将被处理,然后存储,因此后续调用不会受到必须重新调整数据的性能影响。
但对于这种特殊情况,我宁愿通过该物业访问
哼?所以你宁愿做$object->someMemberVariable
与$object->getValueForMember()
?为什么?后者使您能够在单个位置更改数据(如有必要(,而不必将责任放在调用代码上,这可能会导致重复逻辑,这是您不想要的。考虑一个时间戳变量,而不是格式化日期,而不是格式化你调用$object->createdAt
的任何位置,你可以格式化日期一次,然后让它在任何需要它的地方可用。
在你的代码片段中,你已经过度设计了__get
调用b/c,你将其视为具体访问器的包装器。
__get
或__set
调用的最佳用途是在ORM中,它们不会创建潜在的数百个具体访问器,而是通过__call
方法充当它们一样存在。
--更新--
你几乎回答了你自己的问题。你在这里做的是微优化,它的回报递减。但如果你按照你说的去做
首先优化其他领域(数据库性能、文件读取、 将东西保存在记忆中,等等((
您将获得更多的投资回报率。
您可以按照 Alex 在评论中的建议进行操作,也可以向用户对象添加包装器方法:
$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();