Как инженер-программист, одним из моих любимых высказываний всегда было:

В компьютерных науках есть только две сложные вещи: инвалидация кеша и присвоение имен вещам.

— Фил Карлтон

Это шутка, трюизм или и то, и другое? Довольно часто при проектировании (именовании) и отладке всплывает поговорка, почему у вас неправильное значение (кэширование). Что касается именования, например, просмотрите репозиторий Git и подсчитайте количество имен классов, которые заканчиваются на Manager, Handler или Helper. . Что именно входит в обязанности этих классов? (Признание вины: вчера я только что создал новый класс Handler).

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

А тут кеширование. Попытка сделать что-то быстрее, прежде чем мы узнаем, что это медленно, — любимое занятие инженеров. Даже те из нас, кто научился этому на собственном горьком опыте, иногда ловят себя на этом. Для проницательного и комичного взгляда на это посмотрите, почему Дональд Кнут утверждал, что преждевременная оптимизация — корень всех зол.

Возвращаясь к цитате Фила Карлтона, можно принять любую точку зрения. Либо это буквальное наблюдение, либо очень смешная шутка (имейте в виду, что в каждой шутке есть доля правды). В любом случае вывод ясен: большинство вопросов компьютерных наук, наоборот, менее (или не настолько) сложны. Это то, что мы любим называть «разрешимыми» или «разрешимыми» проблемами. При разумном количестве времени мы можем выполнить эти задачи.

Однако, будучи относительно новым участником подкаста Ruby Rogues, на этой неделе я принял участие в обсуждении, которое полностью изменило мое отношение к этому вопросу. Вспыхнула лампочка, и я понял, что реальность программирования означает, что на самом деле мы должны делать противоположные выводы. Почти все в информатике сложно (или, по крайней мере, может быть сделано таким).

Кэширование — это жизнь

Персонаж Дэни Рохас в сериале Тед Лассо любит говорить, что футбол — это жизнь. Во многих смыслах кэширование — это жизнь в программировании. Вот почему цитата о двух сложных проблемах сильно недооценена из-за ее огромного масштаба.

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

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

Я сомневаюсь, покажите мне код

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

# Example 1
a = 10
# Example 2
sum = add(5, 7)
# Example 3
price = get_bitcoin_price_in_usd()

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

Это правда. Это пример кэширования. Однако то же самое можно сказать и о трех приведенных выше примерах кода. Они применяют ту же концепцию в том смысле, что мне не нужно снова делать то, что мне нужно сделать, чтобы получить данные.

Простое присвоение значения константе (Пример №1) не так интересно. Пример #2 всегда дает одно и то же значение. Ответ можно было бы запоминать, еще одна форма кэширования, но в данном случае вряд ли оно того стоит.

Наконец, пример № 3 обеспечивает четкую аналогию для наших целей. Как известно любому, кто следит за криптовалютами, через секунду после того, как вы узнаете цену биткойна, ваш кеш, скорее всего, будет аннулирован, поскольку цена может быстро измениться.

Половина программирования сложная, половина легкая

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

Фил Картон был не только прав, он был прав даже больше, чем, возможно, даже себе представлял. Однако две проблемы, с которыми мы сталкиваемся, не ограничиваются, они являются фундаментальными для нашей дисциплины.

Итак, каков чистый результат этого наблюдения? Мы не можем избавиться от данных, потому что я не могу придумать полезную программу, которая не использует какие-либо данные.

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

Рассмотрим снова наш оператор присваивания для получения цены биткойна. Хотя значение цены, несомненно, будет меняться со временем, мы можем принимать осмысленные решения об этом в течение времени жизни вызова функции, например, следует ли нам покупать, продавать или хранить (это не опечатка, см. предысторию здесь).

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

Или рассмотрите другой (Ruby) фрагмент кода, чтобы проиллюстрировать точку зрения под другим углом.

# Smaller chance of side effects with assingment to a
def my_function
    a = 10
    # do some great stuff
end
# Bigger chance of side effects (dollar sign is a global var)
$a = 10

Без гражданства, как правило, всегда будет проще

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

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

Подпишитесь на меня в Твиттере или похлопайте, если вам понравилось и вы хотели бы узнать больше об упрощении кода (и жизни).