为了澄清,我的意思是:
class foon {
private $barn = null;
public function getBarn() {
if (is_null($this->barn)) {
$this->barn = getBarnImpl();
}
return $this->barn;
}
}
当您并不总是需要getBarn
,并且getBarn
特别昂贵(例如有一个DB调用)时,这一点尤其好。有什么方法可以避免有条件的吗?这占用了很多空间,看起来很难看,看到条件句消失总是很好的。有没有其他模式可以处理我看不到的这种懒惰加载?
通过使用php的__call()
魔术方法,我们可以轻松地编写一个decorator对象,它可以拦截所有方法调用,并缓存返回值。
有一次我做了这样的事:
class MethodReturnValueCache {
protected $vals = array();
protected $obj;
function __construct($obj) {
$this->obj = $obj;
}
function __call($meth, $args) {
if (!array_key_exists($meth, $this->vals)) {
$this->vals[$meth] = call_user_func_array(array($this->obj, $meth), $args);
}
return $this->vals[$meth];
}
}
然后
$cachedFoon = new MethodReturnValueCache(new foon);
$cachedFoon->getBarn();
我不时对此感到好奇,但我肯定想不出。除非您想创建一个单独的函数来处理数组和反射属性查找。
你可以做:
return $this->barn != null ? $this->barn : ($this->barn = self::getBarnImpl());
但我看不出这有什么更好的。
return ( $this->barn = $this->barn ? $this->barn : getBarn() );
或者php 5.3(?)之一:
return ( $this->barn = $this->barn ?: getBarn() );
我想我从来没有见过完全消除这种类型的惰性初始化检查的方法,但想想很有趣。使用toy示例似乎没有任何优势,但在大型对象中,您可以将懒惰初始化行为重构为要初始化的对象,或者(更有趣的是)某种通用的懒惰初始化器模式(我描绘的大致类似于singleton)。基本上,除非他们决定将其构建为一个语言构造(在这种情况下,它仍然存在,只是隐藏的),否则我认为你能做的最好的事情就是自己封装代码。
class LazyObject
{
...
public function __construct($type, $args)
{
$this->type = $type;
...
}
public getInstance()
{
if (empty($this->instance))
$this->instance = new $this->type($args);
return $instance;
}
}
class AggregateObject
{
private $foo;
private $bar;
public function __construct()
{
$this->foo = new LazyObject('Foo');
$this->bar = new LazyObject('Bar');
}
public function getFoo()
{
return $this->foo->getInstance();
}
...
}
方法1
我能想到听众课。
Constructor () {
object = null
listener = new Object() { // this is called once
object = init()
listener = new Object() { // next time
do-nothing() // this is called
}
}
Object get() {
listener.invoke()
return object
这没有条件检查器,但它为每个对象添加了一个额外的字段,有效地减少了内存消耗,而调用无用代码listener.invoke()
的愚蠢惩罚仍然存在。我不知道如何去除所有的多形瘤。由于get()
方法由类的所有实例共享,因此无法对其进行变形。
方法2
通过利用惰性类加载进行Java按需初始化。
底线
因此,看起来替代方案比条件方案更糟糕,因为现代CPU优化了分支预测。所以,我预计,一旦代码被初始化,分支总是朝着一个方向进行,检查惩罚将非常小。在初始化时,false分支将只执行一次,而且与初始化时间相比,它也会很短。否则,您可能不想推迟初始化。