November 7, 2019

Декларативные запросы в Интернете

Пример сравнения декларативных и императивных подходов из книги "Высыко-нагруженные приложения. Программирование, масштабирование, поддержка" Мартина Клеппмана

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

Допустим, у нас есть сайт, посвященный морским животным. Пользователь сейчас просматривает страницу об акулах, так что мы помечаем пункт меню Sharks как выбранный в настоящий момент, вот так:

<ul>
    <li class="selected">
      <p>Sharks</p>
        <ul>
          <li>Great White Shark</li>
          <li>Tiger Shark</li>
          <li>Hammerhead Shark</li>
        </ul>
      </li>
    <li>
      <p>Whales</p>
      <ul>
        <li>Blue Whale</li>72  Часть I. Основы информационных систем
        <li>Humpback Whale</li>
        <li>Fin Whale</li>
      </ul>
    </li>
</ul>
  • Выбранный пункт помечен классом CSS "selected" .
  • Заголовок выбранной в настоящий момент страницы — <p>Sharks</p> .

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

li.selected > p {
  background-color: blue;
}

Тут CSS-селектор li.selected > p объявляет шаблон для элементов, для которых мы выбираем синий стиль: это все элементы <p> , чьим непосредственным родителем является элемент <li> с CSS-классом selected. Элемент <p>Sharks</p> в примере соответствует этому шаблону, а <p>Whales</p> — нет, поскольку у его родительского класса отсутствует class="selected" .

Если же использовать XSL вместо CSS, то можно сделать нечто схожее:

<xsl:template match="li[@class='selected']/p">
  <fo:block background-color="blue">
    <xsl:apply-templates/>
  </fo:block>
</xsl:template>

В этом фрагменте кода XPath-выражение li[@class='selected']/p эквивалентно CSS-селектору li.selected > p из предыдущего примера. XSL и CSS объединяет то, что они оба — декларативные языки описания стилей документа.

Представьте только, как выглядела бы ваша жизнь, если бы пришлось задействовать императивный подход. В JavaScript при использовании базового API объектной модели документа (document object model, DOM) результат выглядел бы примерно так:

var liElements = document.getElementsByTagName("li");
for (var i = 0; i < liElements.length; i++) {
  if (liElements[i].className === "selected") {
    var children = liElements[i].childNodes;
    for (var j = 0; j < children.length; j++) {
      var child = children[j];
      if (child.nodeType === Node.ELEMENT_NODE && child.tagName === "P") {
        child.setAttribute("style", "background-color: blue");
      }
    }
  }
}

JavaScript императивно задает синий цвет фона для элемента <p>Sharks</p> , но код ужасен. Он не только намного длиннее и труднее для понимания, чем эквивалентный код XSL/CSS, но и включает некоторые довольно серьезные проблемы.

При удалении класса selected (например, при щелчке пользователя на другой странице) синий фон останется даже в случае повторного выполнения кода, так что элемент останется выделенным до тех пор, пока не будет перезагружена страница целиком. В CSS браузер автоматически определит, когда правило li.selected > p перестанет действовать и уберет синий фон сразу же при удалении класса selected .

Если вам хотелось бы воспользоваться преимуществами нового API, например document.getElementsByClassName("selected") или даже document.evaluate() — для улучшения производительности, то придется переписать код. С другой стороны, создатели браузеров имеют возможность улучшать производительность CSS и XPath без нарушения совместимости.

В браузере использование декларативных CSS-стилей намного удобнее императивного управления стилями из JavaScript. Аналогично в базах данных декларативные языки запросов, такие как SQL, оказываются намного более удобными, чем императивные API запросов.

Теперь у вас тоже появилоась желание изучать функциональные языкы с декларативным подходом? :D

Статья написана для @response418