Lazy Initialization (отложенная инициализация)

lazy initializationЗдравствуйте, сегодня речь пойдёт об очередном паттерне - lazy initialization (отложенная инициализация). Освоив этот паттерн вы научитесь инициализировать ресурсы только, тогда когда они действительно нужны и тем самым увеличите скорость работы ваших приложений.

Описание

При проектировании вашего приложения вы можете столкнутся с необходимостью создать классы, которые будут выполнять какие-либо ресурсоемкие вычисления. Обычно первым приходящим на ум решением является инициализация объекта и выполнение всех расчётов в момент создания объекта(например в конструкторе), что-бы объект был полностью готовым к использованию. Однако это решение хоть и является очевидным, но уж точно не является оптимальным, т.к. вы не всегда можете быть уверены в том, что результаты работы вашего объекта будут запрошёны тем более вы не можете быть уверены что они будут востребованы полностью. Существует вероятность того, что будут использоваться только 10% из тех данных, которые вы инициализируете при создании объекта, соответственно остальные 90% данных будут ненужны и потраченное на их подготовку время будет безполезным. А представьте если таких объектов инициализируется 100...А если 1000? Вот тут то к нам на помощь приходит паттерн lazy initialization. Суть данного паттерна в том, что данные обрабатываются непосредственно в тот момент в который они запрашиваются. Т.е. когда пользовательский код запрашивает какие-то данные у нашего объекта, мы сначала проверяем не вычесленны ли эти данные ранее, если нет, то вычисляем их сохраняем внутри объекта и возвращаем запрашиваемому коду. Таким образом необходимые данные будут вычислинны тогда и только тогда, когда они будут необходимы. Это позволит значительным образом съекономить время и память, тем более что аренда серверов сейчас стоит совсем не дёшево ;)

Реализация

Давайте рассмотрим реализацию данного шаблона на примере класса, который "лениво" вычисляет факториал:

<?php
class LazyFactorial{
    protected $factorial = array(1);

    protected function addFactorials($n){
        echo 'Add ' . $n . '!' . "\n";
        for ($i = count($this->factorial); $i <= $n; $i++){
            $this->factorial[$i] = $this->factorial[$i-1]*$i;
        }
    }

    public function getFactorial($n){
        if (!array_key_exists($n, $this->factorial)){
            $this->addFactorials($n);
        }
        return $this->factorial[$n];
    }

    public function printFactorials(){
        print_r($this->factorial);
    }
}

В данном примере у нас есть класс, который вычисляет факториал. Как вы видите данный класс никаким образом не инициализируется (что для этого примера очевидно), затем для того что-бы пользователь смог запросить вычисление факториала мы реализуем метод getFactorial, который сначала проверяет небыл ли нужный факториал вычеслен ранее, если нет, то запускает вычисления. Метод отвечающий за вычисления (addFactorials) расширяет массив факториалов до необходимого нам количества. Очевидно, что при работе с данным классом мы во-первых будем вычислять факториал определённого числа только тогда, когда он нам понадобится, во-вторых мы не будем вычислять одно и то же значение дважды. Пример использования класса:

$factorial = new LazyFactorial();
$factorial->printFactorials();
echo $factorial->getFactorial(2) . "\n";
$factorial->printFactorials();
echo $factorial->getFactorial(6) . "\n";
$factorial->printFactorials();
echo $factorial->getFactorial(3) . "\n";
$factorial->printFactorials();

В результате получаем следующий вывод:

Array
(
    [0] => 1
)
Add 2!
2
Array
(
    [0] => 1
    [1] => 1
    [2] => 2
)
Add 6!
720
Array
(
    [0] => 1
    [1] => 1
    [2] => 2
    [3] => 6
    [4] => 24
    [5] => 120
    [6] => 720
)
6
Array
(
    [0] => 1
    [1] => 1
    [2] => 2
    [3] => 6
    [4] => 24
    [5] => 120
    [6] => 720
)

Как вы видите наши ожидания подтвердились. Возможно данный пример не полностью отражает суть паттерна lazy initialization, но основная мысль передаётся точно. Если есть какие-либо вопросы задавайте их в комментариях. Спасибо за внимание!