在延迟加载中避免条件

时间:2012-01-05 22:10:20

标签: php coding-style conditional lazy-loading

只是为了澄清,我的意思是:

class foon {
   private $barn = null;

   public function getBarn() {
      if (is_null($this->barn)) {
         $this->barn = getBarnImpl();
      }
      return $this->barn;
   }
}

如果您不总是需要getBarn,并且getBarn特别昂贵(例如进行数据库调用),这一点尤为出色。有没有办法避免有条件的?这占用了大量空间,看起来很丑陋,看到条件消失总是很好。还有其他一些范例可以处理这种我看不到的延迟加载吗?

6 个答案:

答案 0 :(得分:2)

通过使用php的__call()魔术方法,我们可以轻松编写一个拦截所有方法调用的装饰器对象,并缓存返回值。

有一次我做了这样的事情:

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();

答案 1 :(得分:1)

我不时地想到这一点,但我当然想不出一个。除非您想创建一个函数来处理数组和反射属性查找。

答案 2 :(得分:1)

return ( $this->barn = $this->barn ? $this->barn : getBarn() );

或php 5.3(?)one:

return ( $this->barn = $this->barn ?: getBarn() );

答案 3 :(得分:1)

你可以这样做:

return $this->barn != null ? $this->barn : ($this->barn = self::getBarnImpl());

但我不明白这是怎么回事。

答案 4 :(得分:1)

我认为我从未见过完全消除这种类型的延迟初始化检查的方法,但考虑一下这很有意思。使用玩具样本似乎没有任何优势,但在大型对象中,您可以将延迟初始化行为重构为要初始化的对象或(更有趣的是)某种通用的惰性初始化模式(我正在描绘大致的东西)类似于单身人士)。基本上除非他们决定将它构建为语言结构(在这种情况下它仍然存在,只有隐藏)我认为你能做的最好就是自己封装代码。

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();
    }
    ...
}

答案 5 :(得分:1)

方法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 on-demand initialization通过利用惰性类加载。

底线

因此,看起来替代方案比条件更差,因为现代CPU优化了分支预测。因此,一旦代码初始化并且分支始终朝向一个方向,检查惩罚将非常小。假分支在初始化时只会被采用一次,并且与初始化时间相比也会很短。否则你可能不想推迟初始化。