Я пишу этот блог, чтобы однозначно предотвратить ситуации, подобные той, что была у меня на работе на прошлой неделе. Во время парного программирования с коллегой он предположил, что проблема, с которой мы столкнулись, могла быть связана с отложенной загрузкой. Разговор проходил примерно так:

Коллега: Хм, это могло быть вызвано ленивой загрузкой, но я не совсем помню, как это работает… а вы?

Я: Я ... не знаю, узнал ли я когда-нибудь об этом.

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

Какая польза от отложенной загрузки?

Веб-сайты и приложения, которые мы разрабатываем, заполнены данными, а именно текстом и изображениями. Некоторые из этих данных, например текст html, почти сразу отображаются на нашей странице. Изображения или большие коллекции данных, извлекаемые из внешней базы данных, будут загружаться дольше, используя память и другие ценные ресурсы, которые мы предпочли бы посвятить другим задачам. В идеале мы, как программисты, обеспечиваем отличное взаимодействие с пользователем, минимизируя требования к ресурсам, поэтому выгодно использовать отложенную загрузку, чтобы отображать текст и изображения только тех частей экрана, которые наш пользователь увидит, когда начнет просматривать. Если они нажмут ссылку вверху страницы, мы никогда не загрузим то, что ниже. Если они прокрутятся вниз, мы запустим загрузку дополнительных данных!

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

Это вызовы базы данных сайта для сбора дополнительных данных и отображения старых сообщений. Альтернативой может быть загрузка всех возможных сообщений при первом входе на сайт, что, как мы знаем, непрактично. Поэтому такие сайты, как Twitter, часто используют прослушиватель событий на прокрутке пользователя или кнопку «Загрузить больше сообщений».

Реализация отложенной загрузки

Если вы похожи на меня, то, вероятно, поняли, что знали о ленивой загрузке с самого начала. Мы можем использовать JavaScript для условного выполнения вызовов API и визуализации элементов в нашем объекте модели документа, а также для добавления слушателей для таких событий, как прокрутка. Однако, как и я, вы можете быть сбиты с толку тем фактом, что во многих пояснениях по ленивой загрузке вообще не упоминается JavaScript. Это связано с тем, что этот фреймворк существует еще до того, как JS стал языком Интернета и, следовательно, нуждался в методах реализации для классических объектно-ориентированных языков, таких как C #.

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

Ленивая инициализация

При ленивой инициализации мы инициализируем объект нулевым значением, но добавляем условие в определение класса объекта, чтобы значение было присвоено при вызове метода get. Нам не нужно будет собирать все необходимые данные, пока нас не попросят об этом! Во многих документах, которые я читал, упоминается C #, но, поскольку я не знаю C #, я напишу пример с Java, используя класс Image с атрибутом size:

public class Image {
  …
  public getSize {
    if (this.size == null) {
    // Run logic that queries a database and assigns the result as our size attribute, then returns that value. This method might involve an ID so that we would know which DB object we were looking for.
    }
    return this.size;
  }
}

Только после того, как был вызван метод getSize, мы должны определить фактический размер этого объекта. Если он никогда не вызывается, нам никогда не придется проходить через этот процесс.

Виртуальный прокси

Ленивая инициализация создает объект со значениями, установленными на null, но зачем вообще беспокоиться об инициализации, если мы никогда не можем ссылаться на объект? Виртуальный прокси - это общий объект, который может стоять вместо нашего фактического объекта, если он когда-либо понадобится. Прокси-сервер должен иметь тот же интерфейс, что и будущий объект, чтобы у него были все те же методы. После вызова одного из этих методов они инициируют создание фактического объекта. Итак, если бы у нас было кулинарное приложение с интерфейсом IngredientsList, мы бы создали класс IngredientsListImplementation, чтобы использовать его, когда мы хотели бы создать список ингредиентов. Но до тех пор вместо этого мы будем использовать IngredientsListProxy:

public class IngredientsListProxy implements IngredientsList {
  private IngredientsList ingredientsList;
  
  public List <Ingredient> getIngredients {
    if (ingredientsList == null) {
      System.out.println(“Fetching list of ingredients”);
      ingredientsList = new IngredientsListImplementation();
    }
    return ingredientsList.getIngredients();
  }
}

Наш прокси создает нулевую переменную (IngredientsList), которая просто обладает характеристиками интерфейса IngredientsList. Как только мы попытаемся сослаться на атрибуты этого объекта (которые в настоящее время равны нулю), он инициирует создание другого объекта и вызовет тот же метод для этого объекта, который будет иметь фактические значения!

Призрак

При использовании призрака мы инициализируем наш фактический желаемый объект вместо заполнителя или прокси. Этот объект будет содержать некоторую релевантную информацию, которая нам в конечном итоге понадобится, но другие атрибуты останутся пустыми. Обычно он создается с немногим более чем идентификатором, а затем заполняется данными при вызове одного из его методов. У нас может быть список атрибутов, который выглядит следующим образом:

objectID = uniqid();
objectName = '';
objectImageUrl = '';

Владелец стоимости

Держатель значения - это универсальный объект, методы которого заменяют атрибуты конкретного объекта. Таким образом, вместо того, чтобы присваивать все значения объекту, который может не использоваться при загрузке нашей страницы, мы просто создаем этот универсальный держатель значений. Мы можем инициализировать держатель значения для каждого фактического объекта, на который мы будем ссылаться. Метод держателя стоимости может выглядеть так:

public String getIngredientName {
  if (ingredientName == null) {
    ingredientName = // a method to query the database
  }
  return ingredientName;
}

Упрощение отложенной загрузки

Я лично нашел эти четыре метода сложными, потому что они не имели много общего с типом кода, который я пишу, который чаще всего полагается на JavaScript для взаимодействия с пользователем. С этой целью многие методы отложенной загрузки были отвлечены от нас, как всегда, благодаря новым фреймворкам, расширениям IDE и удобным обновлениям языка. Например, если у нас было изображение HTML, которое мы не хотели загружать, пока пользователь не прокрутил страницу до этой части:

<img src='img1.jpg'>

Мы могли бы сделать это, добавив в код простой атрибут:

<img loading='lazy' src='img1.jpg'>

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

Источники