Избегать условных ленивых загрузок - PullRequest
7 голосов
/ 06 января 2012

Просто чтобы уточнить, я имею в виду что-то вроде:

class foon {
   private $barn = null;

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

Это особенно хорошо, когда вам не всегда нужно getBarn, а getBarn особенно дорого (например, имеет вызов DB). Есть ли способ избежать условного? Это занимает много места, выглядит некрасиво, и видеть исчезновение условных выражений всегда приятно. Есть ли какая-то другая парадигма, чтобы справиться с этой ленивой загрузкой, которую я просто не вижу?

Ответы [ 6 ]

2 голосов
/ 06 января 2012

Используя магический метод 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 голос
/ 26 ноября 2012

Method1

Я могу думать о классе слушателя.

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() является общим для всех экземпляров класса, он не может быть преобразован.

Method2

Инициализация Java по требованию с использованием загрузки ленивых классов.

Нижняя строка

Итак, похожеальтернативы хуже условных, потому что современные процессоры оптимизируют прогнозы ветвлений.Таким образом, штраф за проверку будет очень маленьким, как я ожидаю, после инициализации кода и перехода всегда идти в одном направлении.Ложная ветвь будет принята только один раз, во время инициализации, и она также будет короткой по сравнению с вашим временем инициализации.В противном случае вы можете не захотеть откладывать инициализацию.

1 голос
/ 06 января 2012

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

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 голос
/ 06 января 2012

Вы могли бы сделать:

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

Но я не понимаю, как это лучше.

1 голос
/ 06 января 2012

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

или php 5.3 (?) Один:

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

1 голос
/ 06 января 2012

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

...